Create an API key and return its one-time secret
Creates an API key on the organization and returns its full secret exactly once.
Creates an API key on the organization and returns its full secret exactly once. Provide a name (trimmed and capped at 120 characters) and a non-empty scopes array; scopes accept the fine-grained resource:read / resource:write names as well as the bundles messages, letters, phone, and admin, which expand to their members before validation. The requested scopes must be a subset of the calling key's own scopes — requesting a scope the caller lacks returns 403, and an unrecognized scope returns 400. Pass an optional inboxIds array to pin the key to some of your inboxes: it then sends only as those inboxes, its mailbox reads see only their mail, and it is refused (403) organization-wide changes such as domains, webhooks, inbox creation, agent passport changes, the audit log, and subject erasure; a key that is itself pinned can only grant inboxes it holds. environment (test or live, default live) only labels the key and picks its secret prefix. Unknown body fields are rejected with a 400, so a misspelled restriction never mints an unrestricted key. Only a key whose creator is an organization owner or admin may create keys. Pass an optional allowedIps array of up to 50 IPv4/IPv6 addresses or CIDR ranges to restrict where the key may authenticate from: it is deny-by-default, so a correct secret presented from an unlisted IP is rejected with 403, while omitting the field allows any IP. The secret arrives in the secret field alongside secretShownOnce: true and can never be retrieved again, so store it immediately and identify the key later by its prefix or id. Creation is not deduplicated — each successful call returns a new, independent key.
Scope: keys:write. Send the API key as a bearer token; Scopes lists what each one reaches.
Request
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | no | Optional idempotency key for safe retries of side-effecting operations. (max length 128) |
Body
Required, application/json: CreateApiKeyRequest.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | |
scopes | array of "account:read", "account:write", "admin", "agents:read", "agents:write", "audiences:read", "audiences:write", "buckets:read", "buckets:write", "budgets:read", "budgets:write", "compliance:read", "compliance:write", "contacts:read", "contacts:write", "domains:read", "domains:write", "events:read", "events:write", "inboxes:read", "inboxes:write", "keys:read", "keys:write", "letters", "letters:read", "letters:send", "messages", "messages:read", "messages:send", "phone", "phone:call", "phone:read", "phone:write", "templates:read", "templates:write", "webhooks:read", "webhooks:write" | yes | (min items 1) |
allowedIps | array of string or null | no | Optional IPv4/IPv6 addresses or CIDR ranges (up to 50) the key may authenticate from. Deny-by-default: a correct secret from an unlisted IP is rejected with 403. Omit or null for any-IP. |
environment | "test", "live", null | no | Cosmetic LABEL only; picks the secret prefix (dairo_test_…/dairo_live_…). It does NOT create a sandbox: a test key sends real mail, really delivered and really billed. Defaults to live. (default "live") |
projectId | string or null | no | Optional organization to bind the key to. Defaults to the caller's active project; you must be a member of the one you name. |
inboxIds | array of string (uuid) or null | no | Optional per-inbox restriction: the inbox ids (inboxes.id, up to 100) this key may send as. Enforced server-side on every send, so it binds a raw POST /v1/messages exactly as much as it binds the CLI. Omit, null, or [] for unrestricted (any inbox). A key that is itself restricted may only grant a subset of its own inboxes; a child that requests none inherits the parent's restriction rather than escaping it. |
Response
201 Created. Body: application/json, CreateApiKeyResponse.
| Field | Type | Required | Description |
|---|---|---|---|
object | "api_key" | yes | |
id | string (uuid) | yes | |
name | string | yes | |
prefix | string | yes | |
scopes | array of string | yes | |
status | "active", "revoked" | yes | |
createdAt | string (date-time) | yes | |
lastUsedAt | string (date-time) or null | no | |
allowedIps | array of string or null | no | The key's IP allowlist (IPv4/IPv6 addresses or CIDR ranges), or null when the key authenticates from any IP. |
inboxIds | array of string (uuid) or null | no | The inbox ids (inboxes.id) this key may send as. Empty or null means the key is unrestricted and may send as any inbox in the project — the default, and what every key minted before this field existed has. |
environment | "live", "test" | no | |
secret | string | yes | One-time API key secret. Store it now; it is never shown again. |
secretShownOnce | true | yes |
Errors
Every error has the same body, {"error": {"type", "code", "message", "param"}}; branch on code. Errors lists every code.
| Status | Meaning |
|---|---|
400 | The request is malformed or a value is invalid (invalid_request, invalid_domain, idempotency_key_mismatch). |
401 | The API key is missing or invalid. |
403 | The key lacks the scope this endpoint needs (scope_missing) or may not do this (not_permitted). |
404 | No such resource in this account (resource_not_found). |
422 | The request is well-formed but a field failed validation (validation_failed). |
429 | Rate limit, plan limit, budget or spend cap reached (rate_limited, plan_limit_reached, budget_exceeded, spend_cap_reached). |
500 | An unexpected error; retry with backoff, then contact support (internal_error). |
List API keys
Returns every API key on the organization — both active and revoked — ordered by createdAt descending, newest first.
Revoke the calling API key
Revokes the key that authenticates this request, flipping its status to revoked so its secret can no longer authenticate — the call to make when a script or CLI signs out.