# Addresses

Make addresses, read what they receive, and look one up in full.

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

## Claim an address

`POST /v1/addresses`, called with a key or a person signed in

Makes an address for an authorised company. An admin calls it with an owner key or from address.seleya.ai, or an app acting for one. The answer carries the address's first key, except for an app, whose connection gets the address instead.

| Field | Type | | |
| --- | --- | --- | --- |
| `owner` | string | optional | The company's owner id. Needed only when you run more than one company. |
| `domain` | string | optional | One of the company's address domains. Defaults to its subdomain of seleya.email. |
| `local` | string | required | The name before the @: 1 to 64 of a-z, 0-9, dot, dash and underscore. |
| `display_name` | string | required | The name people see. It must say it is an agent, such as "Acme support agent". |
| `holder_email` | string | optional | The person accountable for the address. They get an email saying so. |
| `label` | string | optional | Names the address's first key, such as "Support agent". |

```bash
curl -X POST https://address.seleya.ai/api/v1/addresses \
  -H "Authorization: Bearer $SELEYA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "local": "desk",
  "display_name": "Acme support agent",
  "holder_email": "ada@acme.com",
  "label": "Support agent"
}'
```

```python
import os, requests

r = requests.post(
    "https://address.seleya.ai/api/v1/addresses",
    headers={"Authorization": f"Bearer {os.environ['SELEYA_KEY']}"},
    json={
      "local": "desk",
      "display_name": "Acme support agent",
      "holder_email": "ada@acme.com",
      "label": "Support agent"
    },
)
print(r.status_code, r.json())
```

```typescript
const r = await fetch("https://address.seleya.ai/api/v1/addresses", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.SELEYA_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "local": "desk",
    "display_name": "Acme support agent",
    "holder_email": "ada@acme.com",
    "label": "Support agent"
  }),
});
console.log(r.status, await r.json());
```

Answers:

- 201: Made
- 401: No valid key, session or token
- 403: Not allowed for this caller
- 409: Conflicts with the current state
- 422: A value is refused; the message says which

| Field (201) | Type | | |
| --- | --- | --- | --- |
| `id` | string | required |  |
| `address` | string | required |  |
| `owner_id` | string | required |  |
| `display_name` | string | required |  |
| `status` | active \| retired | required |  |
| `holder_emails` | array of strings | optional |  |
| `holder_person_id` | string, or null | optional |  |
| `screening` | map of string | optional |  |
| `capabilities` | Capabilities | optional |  |
| `approvals` | off \| unknown_recipients \| everything | optional |  |

```json
{
  "address": "first@acme.seleya.email",
  "display_name": "First agent",
  "holder_emails": [],
  "holder_person_id": null,
  "id": "7f3c9a2e-0000-4000-8000-000000000028",
  "key": {
    "id": "7f3c9a2e-0000-4000-8000-000000000029",
    "secret": "sa_test_…"
  },
  "owner_id": "7f3c9a2e-0000-4000-8000-000000000020",
  "screening": {},
  "status": "active"
}
```

## Your addresses

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

The addresses the caller can act for.

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

```python
import os, requests

r = requests.get(
    "https://address.seleya.ai/api/v1/addresses",
    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/addresses", {
  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
- 403: Not allowed for this caller

| Field (200) | Type | | |
| --- | --- | --- | --- |
| `addresses` | array of Address | required |  |

```json
{
  "addresses": [
    {
      "address": "ada@beta.com",
      "display_name": "Ada's agent",
      "holder_emails": [
        "holder@beta.com"
      ],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000001",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ada@beta.com",
      "display_name": "Ada's agent",
      "holder_emails": [
        "holder@beta.com"
      ],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000003",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ada@beta.com",
      "display_name": "Ada's agent",
      "holder_emails": [
        "holder@beta.com"
      ],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000004",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ada@beta.com",
      "display_name": "Ada's agent",
      "holder_emails": [
        "holder@beta.com"
      ],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000005",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ada@beta.com",
      "display_name": "Ada's agent",
      "holder_emails": [
        "holder@beta.com"
      ],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000006",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ada@beta.com",
      "display_name": "Ada's agent",
      "holder_emails": [
        "holder@beta.com"
      ],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000007",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "bob@beta.com",
      "display_name": "Bob's agent",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000008",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "bob@beta.com",
      "display_name": "Bob's agent",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000009",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "bob@beta.com",
      "display_name": "Bob's agent",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-00000000000a",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "bob@beta.com",
      "display_name": "Bob's agent",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-00000000000b",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "bob@beta.com",
      "display_name": "Bob's agent",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-00000000000c",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "bob@beta.com",
      "display_name": "Bob's agent",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-00000000000d",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-a@beta.com",
      "display_name": "Contract agent A",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-00000000000e",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-a@beta.com",
      "display_name": "Contract agent A",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-00000000000f",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-a@beta.com",
      "display_name": "Contract agent A",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000010",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-a@beta.com",
      "display_name": "Contract agent A",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000011",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-a@beta.com",
      "display_name": "Contract agent A",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "ba751b6e-6435-4357-ad39-6df26a",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-a@beta.com",
      "display_name": "Contract agent A",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000012",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-a@beta.com",
      "display_name": "Contract agent A",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000013",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-a@beta.com",
      "display_name": "Contract agent A",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000014",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-b@beta.com",
      "display_name": "Contract agent B",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000015",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-b@beta.com",
      "display_name": "Contract agent B",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000016",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-b@beta.com",
      "display_name": "Contract agent B",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000017",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-b@beta.com",
      "display_name": "Contract agent B",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000018",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-b@beta.com",
      "display_name": "Contract agent B",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-000000000019",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-b@beta.com",
      "display_name": "Contract agent B",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-00000000001a",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-b@beta.com",
      "display_name": "Contract agent B",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-00000000001b",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    },
    {
      "address": "ct-b@beta.com",
      "display_name": "Contract agent B",
      "holder_emails": [],
      "holder_person_id": null,
      "id": "7f3c9a2e-0000-4000-8000-00000000001c",
      "owner_id": "7f3c9a2e-0000-4000-8000-000000000002",
      "screening": {},
      "status": "active"
    }
  ]
}
```

## An address in full

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

An address with its holder, lists, keys, connected apps, the approvals waiting and today's quota.

| Parameter | Type | | |
| --- | --- | --- | --- |
| `a` | string, in path | required | The address, such as desk@acme.seleya.email. |

```bash
curl https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email \
  -H "Authorization: Bearer $SELEYA_KEY"
```

```python
import os, requests

r = requests.get(
    "https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email",
    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/addresses/desk@acme.seleya.email", {
  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
- 403: Not allowed for this caller
- 404: Not found, or not yours to see

| Field (200) | Type | | |
| --- | --- | --- | --- |
| `id` | string | required |  |
| `address` | string | required |  |
| `owner_id` | string | required |  |
| `display_name` | string | required |  |
| `status` | active \| retired | required |  |
| `holder_emails` | array of strings | optional |  |
| `holder_person_id` | string, or null | optional |  |
| `screening` | map of string | optional |  |
| `capabilities` | Capabilities | optional |  |
| `approvals` | string | optional |  |
| `holder` | string, or null | optional |  |
| `senders` | array of objects | optional |  |
| `bindings` | array of Binding | optional |  |
| `pending_approvals` | integer | optional |  |
| `connections` | array of objects | optional |  |
| `quota` | object | optional |  |
| `company` | object | optional |  |

```json
{
  "address": "apps@seleya.me",
  "approvals": "off",
  "bindings": [],
  "company": null,
  "connections": [
    {
      "app": "Seleya Address test client",
      "id": "7f3c9a2e-0000-4000-8000-00000000001d",
      "last_used_at": "2026-10-22T00:40:34.644228+00:00",
      "returns_to": "127.0.0.1",
      "since": "2026-10-22T00:40:33.739498+00:00"
    }
  ],
  "display_name": "Test's agent",
  "holder": "apps@acme.com",
  "holder_emails": [
    "apps@acme.com"
  ],
  "holder_person_id": "7f3c9a2e-0000-4000-8000-00000000001e",
  "id": "d4cd2e17-2261-4177-91ae-4b",
  "owner_id": "7f3c9a2e-0000-4000-8000-00000000001f",
  "pending_approvals": 1,
  "quota": {
    "address": {
      "limit": 50,
      "used": 0
    },
    "company": {
      "limit": 500,
      "used": 0
    }
  },
  "screening": {},
  "senders": [
    {
      "action": "block",
      "entry": "spam@example.com"
    }
  ],
  "status": "active"
}
```

## Read by cursor

`GET /v1/addresses/{a}/messages`, called with a key or a person signed in

Delivered messages after a sequence number, oldest first. Pass next_after back as after to read on, and no message is ever missed. Held mail never appears here.

| Parameter | Type | | |
| --- | --- | --- | --- |
| `a` | string, in path | required | The address, such as desk@acme.seleya.email. |
| `after` | integer, in query | optional | The sequence number to read after. 0, or left out, starts at the first message. |
| `limit` | integer, in query | optional | How many to answer: 1 to 100. Defaults to 50. |
| `wait` | integer, in query | optional | Seconds to wait for the next message when none is there yet: 0 to 20. |

```bash
curl https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email/messages?after=0&wait=20 \
  -H "Authorization: Bearer $SELEYA_KEY"
```

```python
import os, requests

r = requests.get(
    "https://address.seleya.ai/api/v1/addresses/desk@acme.seleya.email/messages?after=0&wait=20",
    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/addresses/desk@acme.seleya.email/messages?after=0&wait=20", {
  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
- 422: bad_cursor or bad_query

| Field (200) | Type | | |
| --- | --- | --- | --- |
| `messages` | array of Message | required |  |
| `next_after` | integer | required |  |

```json
{
  "messages": [],
  "next_after": 0
}
```

## One delivery

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

One delivered message. Held mail is shown only to a person.

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

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

```python
import os, requests

r = requests.get(
    "https://address.seleya.ai/api/v1/deliveries/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/deliveries/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 | The delivery's id: pass it as in_reply_to to reply. |
| `seq` | integer, or null | optional | Its place in the address's stream; read after it to go on. |
| `address` | string | required | The address that received it. |
| `conversation_id` | string | optional | The thread it belongs to. |
| `screening` | string | required | delivered, held or rejected. |
| `trust` | Trust | required | The verdict on its sender. |
| `message` | object | required | The message itself. |
| `received_at` | string | required | When it arrived. |

```json
{
  "address": "mx-b@beta.com",
  "conversation_id": "7f3c9a2e-0000-4000-8000-000000000022",
  "id": "7f3c9a2e-0000-4000-8000-000000000023",
  "message": {
    "cc": [],
    "from": "mx-a@beta.com",
    "id": "7f3c9a2e-0000-4000-8000-000000000024",
    "in_reply_to": "",
    "message_id_header": "<010701a161094821-7f3c9a2e-0000-4000-8000-000000000025@eu-central-1.amazonses.com>",
    "parts": [
      {
        "kind": "text",
        "text": "Shall we meet on Tuesday at 10:00?"
      }
    ],
    "references": [],
    "subject": "Mixed 10220035",
    "to": [
      "person@beta.com",
      "mx-b@beta.com"
    ],
    "transport": "mixed"
  },
  "received_at": "2026-10-22T00:36:00.900113+00:00",
  "screening": "delivered",
  "seq": 1,
  "trust": {
    "class": "known",
    "evidence": {
      "dkim": "PASS",
      "dmarc": "PASS",
      "dmarc_policy": "",
      "spam": "PASS",
      "spf": "PASS",
      "transport": "native",
      "virus": "PASS",
      "why": "a contact of this address; sent natively by an address on the service"
    },
    "untrusted": false
  }
}
```

