Skip to content

Using the API

Everything lives under one base URL and takes one header.

https://mail.yourdomain.com/api/v1
Authorization: Bearer mk_live_...

Make a key under Settings → API keys. See API keys for what the options mean.

The first call

sh
curl https://mail.yourdomain.com/api/v1/me \
  -H "Authorization: Bearer mk_live_..."
json
{
  "object": "api_key",
  "id": "key_…",
  "name": "Production",
  "scopes": ["*"],
  "organization": { "id": "org_…", "name": "Acme" },
  "reach": { "unrestricted": true, "domains": [], "mailboxes": [] },
  "reachable_mailboxes": 4,
  "rate_limit_per_minute": 300,
  "api": {
    "version": "v1",
    "base_url": "https://mail.yourdomain.com/api/v1",
    "all_scopes": ["emails:send", "…"],
    "webhook_events": ["mail.received", "…"]
  }
}

Make this call first whenever something is not working. It tells a missing scope apart from a wrong URL in one line, and it needs no scope of its own.

The shape of a reply

Every object carries an object field, so a value can be told apart without knowing which call returned it.

json
{ "object": "message", "id": "msg_…", "subject": "Hello" }

Every list is the same shape:

json
{
  "object": "list",
  "data": [  ],
  "has_more": true,
  "next_cursor": "1767225845000|thr_…"
}

See Pagination.

Every failure is the same shape:

json
{ "error": "No such thread", "code": "not_found" }

See Errors.

Conventions

  • snake_case in and out. The database is camelCase; the API is not.
  • Timestamps are ISO 8601 strings in UTC.
  • Addresses are { "name": "Ada", "address": "ada@example.com" } when read, and a plain string or array of strings when written. Ada <ada@example.com> is understood.
  • Ids are prefixedmsg_, thr_, mbx_, lbl_, whk_ — so a wrong id in the wrong place is obvious.
  • Many lookups take a name as well as an id. GET /domains/example.com, GET /labels/Receipts and DELETE /suppressions/bad@example.net all work.
  • Bodies are at most 30 MB, which is what caps attachments.

Every endpoint

MethodPathScope
GET/meany key
POST/emailsemails:send
POST/emails/batchemails:send
GET/emailsemails:read
GET/emails/:idemails:read
GET/threadsmail:read
GET/threads/:idmail:read
PATCH/threads/:idmail:write
DELETE/threads/:idmail:write
POST/threads/:id/replymail:write
GET/messagesmail:read
GET/messages/:idmail:read
PATCH/messages/:idmail:write
GET/messages/:id/rawmail:read
GET/attachments/:idmail:read
GET/mailboxesmailboxes:read
POST/mailboxesmailboxes:write
GET/mailboxes/:idmailboxes:read
PATCH/mailboxes/:idmailboxes:write
DELETE/mailboxes/:idmailboxes:write
GET/domainsdomains:read
POST/domainsdomains:write
GET/domains/:iddomains:read
POST/domains/:id/verifydomains:write
DELETE/domains/:iddomains:write
GET/labelslabels:read
POST/labelslabels:write
GET/labels/:idlabels:read
PATCH/labels/:idlabels:write
DELETE/labels/:idlabels:write
GET/contactscontacts:read
GET/suppressionssuppressions:read
POST/suppressionssuppressions:write
DELETE/suppressions/:idsuppressions:write
GET/webhookswebhooks:read
POST/webhookswebhooks:write
GET/webhooks/:idwebhooks:read
PATCH/webhooks/:idwebhooks:write
DELETE/webhooks/:idwebhooks:write
POST/webhooks/:id/pingwebhooks:write
GET/webhook-deliverieswebhooks:read
POST/webhook-deliveries/:id/replaywebhooks:write
GET/statsstats:read

Prefer not to write this by hand?

The Node SDK wraps all of it, typed, with retries, pagination and webhook signature checking already done.

Released under the MIT Licence.