Give your AI agent an email inbox in one call

Send one unauthenticated POST to https://getcourier.dev/alias, with no body and no API key, and GetCourier returns a working inbox: an address on getcourier.dev, a read_token, a 72-hour expiry and an Ed25519 DID identity. The call takes about 15 ms and answers 201. The inbox receives mail; it does not send.

The call

curl -s -X POST https://getcourier.dev/alias

Every field is optional. To label the inbox, send a JSON body:

curl -s -X POST https://getcourier.dev/alias \
  -H "Content-Type: application/json" \
  -d '{"purpose":"signing up for an API key","agent":"build-bot-7","service":"generic","alias":"my-agent","ttl_hours":72}'

POST /v1/sandbox/inboxes is the same handler with the same body, 201 shape and errors.

The response

201 (payment fields omitted):

{
  "success": true,
  "address": "my-agent-k3xmqz@getcourier.dev",
  "inbox_id": "5b40d4d6-c046-46c9-aaa1-d0f1e20194e0",
  "read_token": "<read_token>",
  "expires_at": "2026-10-26T12:00:00.000Z",
  "ttl_hours": 72,
  "paid": false,
  "identity": {
    "did": "did:key:z6Mk...",
    "public_key": "<base64url ed25519 public key>",
    "fingerprint": "9f2c4b1d0a7e5566",
    "private_key": "<base64url pkcs8 private key - shown once>"
  },
  "links": {
    "messages": "/inbox/5b40d4d6-c046-46c9-aaa1-d0f1e20194e0/messages",
    "wait": "/v1/sandbox/inboxes/my-agent-k3xmqz/wait",
    "mcp": "/mcp"
  },
  "note": "read_token and identity.private_key are shown once"
}

Paid creations additionally return charged_credits and balance once the credit rails are live.

What the read_token and identity are

read_token is the credential for reading that one inbox, sent as Authorization: Bearer <read_token>. It is inbox-scoped: a valid token for a different inbox gets 403 NOT_OWNER, and a missing or unknown token gets 401 UNAUTHORIZED. Tokens are never accepted in the query string. It is shown once, so store it before the response is thrown away.

identity is an Ed25519 keypair minted per inbox and expressed as a DID (did:key:z6Mk...), with a base64url public key and a short fingerprint. The private key (base64url PKCS#8) is returned once. The reference does not document the identity as a way to authenticate to the inbox - the read_token is what authorises reads - so treat the key as an identifier you hold, not a login.

Python (urllib)

import json, urllib.request

req = urllib.request.Request(
    "https://getcourier.dev/alias",
    data=b"{}",
    method="POST",
    headers={"Content-Type": "application/json"},
)
with urllib.request.urlopen(req) as r:
    inbox = json.load(r)

print(inbox["address"], inbox["read_token"])

Node (fetch)

const res = await fetch("https://getcourier.dev/alias", { method: "POST" });
const inbox = await res.json();

console.log(inbox.address, inbox.read_token);

POST with no body works here too: the endpoint does not require one.

Reading mail from the inbox

curl -s https://getcourier.dev/inbox/<inbox_id>/messages \
  -H "Authorization: Bearer <read_token>"

Or by alias, with paging: GET /v1/sandbox/inboxes/{alias}/messages?after=<message id>&limit=20 (limit default 20, max 100). To block until mail arrives, use GET /v1/sandbox/inboxes/{alias}/wait.

Limits

FAQ

Do I need an API key to create an inbox?

No. POST /alias takes no auth. You may optionally send Authorization: Bearer cak_... to buy a longer-lived inbox with credits; without it the inbox is free and expires after 72 hours.

What happens when the inbox expires?

expires_at gives the time; the free lifetime is 72 hours. A message that "does not exist or has expired" returns 404 NOT_FOUND. Delete an inbox early with DELETE /v1/sandbox/inboxes/{alias}, which needs the matching read_token.

Can I choose the address?

Partly. You supply the prefix with alias, but a 6-character random suffix is always appended, so my-agent becomes my-agent-k3xmqz@getcourier.dev.