# Approvals

What an agent asked that only a person may do, and the sends a person wanted to see first.

Calls go to https://address.seleya.ai/api. Errors are `{"error": {"code", "message"}}` with the HTTP status: https://address.seleya.ai/docs/reference/errors.md

## Waiting for a person

`GET /v1/approvals`, called with a key or a person signed in

What waits for a person: sends held for approval, and person-only actions agents asked for.

| Parameter | Type | | |
| --- | --- | --- | --- |
| `address` | string, in query | optional | One address. |
| `all` | string, in query | optional | true includes those already decided. |

```bash
curl https://address.seleya.ai/api/v1/approvals \
  -H "Authorization: Bearer $SELEYA_KEY"
```

```python
import os, requests

r = requests.get(
    "https://address.seleya.ai/api/v1/approvals",
    headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
)
print(r.status_code, r.json())
```

```typescript
const r = await fetch("https://address.seleya.ai/api/v1/approvals", {
  method: "GET",
  headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}` },
});
console.log(r.status, await r.json());
```

Answers:

- 200: Done
- 401: No valid key, session or token
- 404: Not found, or not yours to see

| Field (200) | Type | | |
| --- | --- | --- | --- |
| `approvals` | array of Approval | required |  |

```json
{
  "approvals": [
    {
      "id": "7f3c9a2e-0000-4000-8000-000000000001",
      "kind": "send",
      "summary": "send a meeting invitation to sam@beta.example",
      "address": "desk@acme.seleya.email",
      "company": "Acme",
      "asked_by": "The app Example agent",
      "status": "pending",
      "decided_at": null,
      "created_at": "2026-10-22T12:00:00Z",
      "request": {
        "to": [
          "sam@beta.example"
        ],
        "cc": [],
        "subject": "Meeting",
        "text": "Can we meet on Tuesday?",
        "transport": "auto"
      }
    }
  ]
}
```

## An ask from its emailed link

`POST /v1/approvals/by-link`, called with 

What an emailed link asks, as the confirm page shows it.

Called by the console's own page, signed in; not for agents.

| Field | Type | | |
| --- | --- | --- | --- |
| `token` | string | required | The token from the emailed link's fragment. |

Answers:

- 200: Done
- 404: Not found, or not yours to see

| Field (200) | Type | | |
| --- | --- | --- | --- |
| `id` | string | required |  |
| `kind` | send \| release \| allow_sender \| unblock_sender \| screening \| approvals_mode \| set_holder \| retire \| add_admin \| remove_admin \| add_contacts \| hard_stop_off | required |  |
| `summary` | string, or null | optional |  |
| `address` | string, or null | optional |  |
| `company` | string, or null | optional |  |
| `asked_by` | string | optional |  |
| `status` | pending \| sending \| approving \| approved \| rejected \| failed \| prepared | required |  |
| `decided_at` | string, or null | optional |  |
| `created_at` | string | required |  |
| `request` | object | optional |  |

## Decide from the link

`POST /v1/approvals/by-link/decide`, called with 

The confirm page's button. Opening the link decides nothing.

Called by the console's own page, signed in; not for agents.

| Field | Type | | |
| --- | --- | --- | --- |
| `token` | string | required | The token from the emailed link's fragment. |
| `decision` | approve \| reject | required | approve carries it out; reject drops it. |

Answers:

- 200: Done
- 403: Not allowed for this caller
- 404: Not found, or not yours to see
- 409: Conflicts with the current state
- 422: A value is refused; the message says which

## One ask

`GET /v1/approvals/{id}`, called with a key or a person signed in

One ask, with what was asked and where it stands.

| Parameter | Type | | |
| --- | --- | --- | --- |
| `id` | string, in path | required | Its id. |

```bash
curl https://address.seleya.ai/api/v1/approvals/ID \
  -H "Authorization: Bearer $SELEYA_KEY"
```

```python
import os, requests

r = requests.get(
    "https://address.seleya.ai/api/v1/approvals/ID",
    headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
)
print(r.status_code, r.json())
```

```typescript
const r = await fetch("https://address.seleya.ai/api/v1/approvals/ID", {
  method: "GET",
  headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}` },
});
console.log(r.status, await r.json());
```

Answers:

- 200: Done
- 401: No valid key, session or token
- 404: Not found, or not yours to see

| Field (200) | Type | | |
| --- | --- | --- | --- |
| `id` | string | required |  |
| `kind` | send \| release \| allow_sender \| unblock_sender \| screening \| approvals_mode \| set_holder \| retire \| add_admin \| remove_admin \| add_contacts \| hard_stop_off | required |  |
| `summary` | string, or null | optional |  |
| `address` | string, or null | optional |  |
| `company` | string, or null | optional |  |
| `asked_by` | string | optional |  |
| `status` | pending \| sending \| approving \| approved \| rejected \| failed \| prepared | required |  |
| `decided_at` | string, or null | optional |  |
| `created_at` | string | required |  |
| `request` | object | optional |  |

```json
{
  "id": "7f3c9a2e-0000-4000-8000-000000000001",
  "kind": "send",
  "summary": "send a meeting invitation to sam@beta.example",
  "address": "desk@acme.seleya.email",
  "company": "Acme",
  "asked_by": "The app Example agent",
  "status": "pending",
  "decided_at": null,
  "created_at": "2026-10-22T12:00:00Z",
  "request": {
    "to": [
      "sam@beta.example"
    ],
    "cc": [],
    "subject": "Meeting",
    "text": "Can we meet on Tuesday?",
    "transport": "auto"
  }
}
```

## Approve

`POST /v1/approvals/{id}/approve`, called with a person signed in

Approves and carries it out. A person only.

Called by the console's own page, signed in; not for agents.

| Parameter | Type | | |
| --- | --- | --- | --- |
| `id` | string, in path | required | Its id. |

Answers:

- 200: Done
- 401: No valid key, session or token
- 403: Not allowed for this caller
- 404: Not found, or not yours to see
- 409: Conflicts with the current state

## Reject

`POST /v1/approvals/{id}/reject`, called with a person signed in

Refuses it; nothing is done. A person only.

Called by the console's own page, signed in; not for agents.

| Parameter | Type | | |
| --- | --- | --- | --- |
| `id` | string, in path | required | Its id. |

Answers:

- 200: Done
- 401: No valid key, session or token
- 403: Not allowed for this caller
- 404: Not found, or not yours to see
- 409: Conflicts with the current state

| Field (200) | Type | | |
| --- | --- | --- | --- |
| `id` | string | required |  |
| `kind` | send \| release \| allow_sender \| unblock_sender \| screening \| approvals_mode \| set_holder \| retire \| add_admin \| remove_admin \| add_contacts \| hard_stop_off | required |  |
| `summary` | string, or null | optional |  |
| `address` | string, or null | optional |  |
| `company` | string, or null | optional |  |
| `asked_by` | string | optional |  |
| `status` | pending \| sending \| approving \| approved \| rejected \| failed \| prepared | required |  |
| `decided_at` | string, or null | optional |  |
| `created_at` | string | required |  |
| `request` | object | optional |  |

