API referenceAPI Keys

Create an API key and return its one-time secret

Creates an API key on the organization and returns its full secret exactly once.

Endpoint
POST https://api.dairo.app/v1/api-keys

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

cURL
curl -X POST "https://api.dairo.app/v1/api-keys" \
  -H "Authorization: Bearer $DAIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Ada Lovelace",
    "scopes": [
      "account:read"
    ]
  }'

Headers

NameTypeRequiredDescription
Idempotency-KeystringnoOptional idempotency key for safe retries of side-effecting operations. (max length 128)

Body

Required, application/json: CreateApiKeyRequest.

FieldTypeRequiredDescription
namestringyes
scopesarray 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)
allowedIpsarray of string or nullnoOptional 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", nullnoCosmetic 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")
projectIdstring or nullnoOptional organization to bind the key to. Defaults to the caller's active project; you must be a member of the one you name.
inboxIdsarray of string (uuid) or nullnoOptional 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.

FieldTypeRequiredDescription
object"api_key"yes
idstring (uuid)yes
namestringyes
prefixstringyes
scopesarray of stringyes
status"active", "revoked"yes
createdAtstring (date-time)yes
lastUsedAtstring (date-time) or nullno
allowedIpsarray of string or nullnoThe key's IP allowlist (IPv4/IPv6 addresses or CIDR ranges), or null when the key authenticates from any IP.
inboxIdsarray of string (uuid) or nullnoThe 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
secretstringyesOne-time API key secret. Store it now; it is never shown again.
secretShownOncetrueyes
201
{
  "object": "api_key",
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "Ada Lovelace",
  "prefix": "dairo_test_abc123",
  "scopes": [
    "string"
  ],
  "status": "active",
  "createdAt": "2026-10-19T09:30:00Z",
  "lastUsedAt": "2026-10-19T09:30:00Z",
  "allowedIps": [
    "string"
  ],
  "inboxIds": [
    "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  ],
  "environment": "live",
  "secret": "string",
  "secretShownOnce": true
}

Errors

Every error has the same body, {"error": {"type", "code", "message", "param"}}; branch on code. Errors lists every code.

StatusMeaning
400The request is malformed or a value is invalid (invalid_request, invalid_domain, idempotency_key_mismatch).
401The API key is missing or invalid.
403The key lacks the scope this endpoint needs (scope_missing) or may not do this (not_permitted).
404No such resource in this account (resource_not_found).
422The request is well-formed but a field failed validation (validation_failed).
429Rate limit, plan limit, budget or spend cap reached (rate_limited, plan_limit_reached, budget_exceeded, spend_cap_reached).
500An unexpected error; retry with backoff, then contact support (internal_error).

On this page