Primitive REST API reference

The Primitive API lives under https://api.primitive.dev/v1. Use Bearer auth with an organization API key, OAuth access token, or restricted agent connection key.

curl https://api.primitive.dev/v1/account \
  -H "Authorization: Bearer $PRIMITIVE_AUTH_TOKEN"

Set PRIMITIVE_AUTH_TOKEN to an API key (prim_...) or OAuth access token (prim_oat_...).

Authentication

Send the credential in the Authorization header:

Authorization: Bearer prim_...
Authorization: Bearer prim_oat_...

Use API keys for server-to-server integrations and CI. Use OAuth for CLI, desktop, local-agent, and third-party app access. OAuth access tokens are short-lived and can be revoked from Settings -> Connected Apps.

Never put API keys, OAuth access tokens, or OAuth refresh tokens in client-side code, browser local storage, screenshots, or logs.

Roles and permissions

Access is governed by organization role-based access control. Every member of an organization holds one of three roles — owner, admin, or member — and a credential inherits a role:

  • Organization API keys always act with member-level access, regardless of the role of the user who created them. An API key can never perform owner- or admin-only actions.
  • OAuth access tokens act with the authorizing user's current organization role, resolved on each request — so if that user's role later changes, the token's access follows it.

Organization API keys and OAuth tokens can call the member-level REST surface. Connection management requires an organization owner or admin session or OAuth token. Agent connection keys (pconn_) permit only their bound address mail operations, reads and writes of their own address notes, and reads of other organization agents' AGENT_WORKING, AGENT_RUNTIME and AGENT_INFO notes. All other operations are denied. Billing reads and organization settings are person-only operations (see below).

Mailbox access

Mailbox access says who may read, draft and send for each mailbox or domain. It can be read and changed through the API, with one exception to the rule above: an organization-wide API key may manage it.

  • GET /v1/mailbox-access lists every access entry. GET /v1/groups lists the groups an entry can name.
  • PUT /v1/mailboxes/{address}/access replaces the access list of one mailbox, or of a domain (@example.com).
  • POST /v1/groups, PATCH /v1/groups/{id} and DELETE /v1/groups/{id} manage groups. Through an API key or the CLI, only groups without a role.

These accept a dashboard session, the person's own Primitive CLI, or an organization-wide API key. A key acts for the person it belongs to, with that person's current access, so it can do here exactly what that person can: reading needs members:read (every member has it), and changing mailbox access needs delegations:manage (owners and admins), so a key of someone demoted to member can still read but no longer change anything. A key whose person has left the organization is refused. Narrowed keys, Function keys and agent connection keys are refused. Any other OAuth app gets step_up_required on a change until the person confirms it in the dashboard. Nobody can grant access beyond their own, and an API key cannot give access to a member's personal mailbox. Every change is recorded in the credential audit log.

Roles and members

Roles, member roles, removing a member, the mailbox access mode, the auto-join setting, break-glass, transfers and access suspensions are in the API reference under "Roles and members". Reads such as GET /v1/roles and GET /v1/access-mode are open to any person. Every change is person only: a dashboard session, or the person's own OAuth or CLI token after they confirm the change in the dashboard. A request without that confirmation answers step_up_required with the purpose and the confirmation link; send the token it shows in the Primitive-Step-Up header. API keys cannot use these routes.

Agent connection keys provide a fixed address-scoped grant through POST /v1/agent-connections and a one-use claim. Arbitrary per-key scopes, such as configurable send-only or read-only grants, are not exposed. See agent connection authentication.

Person-only operations

Some operations act on the organization's people and credentials rather than its mail, so only a person can call them. API keys, agent connection keys and agents are refused. Each is marked in the OpenAPI document by x-primitive-person-only and says who may call it:

  • Dashboard session: a person signed in to the dashboard.
  • Session or the person's OAuth token: the dashboard, or the person's own OAuth token (the CLI or an app they connected).
  • Session or the person's CLI: the dashboard, or the person's own Primitive CLI token; no other OAuth app.
  • Step-up: the dashboard, or the person's own OAuth token carrying a step-up token in the Primitive-Step-Up header. Without one the API answers 403 step_up_required, and error.details.step_up_url opens the dashboard page that issues a single-use token for that purpose, valid for five minutes. The token works only with the app or CLI it was confirmed for, and is never accepted with a token issued for the MCP endpoint or with an API key. An operation whose marker also carries cli_without_step_up takes the person's own Primitive CLI login with no step-up, unless the organization's credential policy sets cli_writes_require_approval.

The person's role must still hold the operation's permission (x-primitive-permission).

Dashboard only

A few things stay in the dashboard on purpose, because they prove a person is present in a browser or cannot be undone:

  • Issuing a step-up token, and approving a CLI sign-in, a Primitive app sign-in or an OAuth app's consent.
  • Confirming or discarding a request that waits for an owner's confirmation.
  • Deleting the organization.
  • Changing the plan or the payment method (checkout and the billing portal). Billing can be read through the API.
  • Onboarding steps, terms acceptance and similar dashboard flows that are not product settings.

Revoking every credential at once (POST /credentials/revoke-all) is in the API but needs an owner in a dashboard session.

OAuth

Primitive supports OAuth Authorization Code + PKCE for public clients. Dynamic Client Registration is available so local tools can obtain a client_id without manual setup.

Discovery and token endpoints:

GET  /.well-known/oauth-authorization-server
POST /oauth/register
GET  /oauth/authorize
POST /oauth/token
POST /oauth/revoke

OAuth grants authorize the selected organization at the authorizing user's current role (see Roles and permissions), resolved on each request: a granted token can call every REST API operation available to that user's role. Fine-grained per-scope OAuth grants — narrowing a token to a subset of operations — are not exposed yet.

Response Envelope

Successful responses use:

{
  "success": true,
  "data": {},
  "meta": { "total": 1, "limit": 50, "cursor": null }
}

Errors use:

{
  "success": false,
  "error": {
    "code": "not_found",
    "message": "Email not found",
    "request_id": "req_..."
  }
}

Branch on stable error.code values rather than human-readable messages.

Pagination

List endpoints use cursor pagination. Pass limit and cursor query parameters. When meta.cursor is null, there are no more pages.

Endpoint Inventory

AreaEndpoints
AccountGET/PATCH /account, GET /account/storage, GET /account/webhook-secret, POST /account/webhook-secret/rotate
CreditsGET /credits/balance, POST /credits/redeem. See Credits.
DomainsGET/POST /domains, PATCH/DELETE /domains/{id}, POST /domains/{id}/dns/check, POST /domains/{id}/verify, GET /domains/{id}/zone-file
Inbound emailsGET /emails, GET /emails/search, GET /emails/{id}, GET /emails/{id}/readable (what an email says, as paged plain text), GET /emails/{id}/conversation, raw/attachments/discard/replay/reply actions. See Search.
ThreadsGET /threads/{id} — the full message chain an inbound email belongs to.
SendingPOST /send-mail, POST /send-mail/batch, POST /sendability, GET /send-permissions, GET /sent-emails, GET /sent-emails/{id}, PATCH /sent-emails/{id} (reschedule a scheduled send), POST /sent-emails/{id}/cancel, GET /outbound/status. send-permissions lists Primitive-managed domains, your domains, known addresses, and member emails. See Sending.
Simple EmailPOST /simple-email/emails, POST /simple-email/emails/batch, GET/PATCH /simple-email/emails/{id}, POST /simple-email/emails/{id}/cancel. A drop-in send surface compatible with a widely used JavaScript email SDK's wire format; it does not use the envelope described on this page. See Simple Email.
MemoriesPUT/GET/DELETE /memories, GET /memories/search. Durable JSON key-value state with org and Function scope. See Primitive Memories.
RoutingGET/POST /routes, POST /routes/reorder, POST /routes/simulate, PATCH/DELETE /routes/{id}. See Routing.
Endpointswebhook endpoint CRUD (GET/POST /endpoints, PATCH/DELETE /endpoints/{id}) plus POST /endpoints/{id}/test. See Endpoints.
Filtersallow/block filter CRUD (GET/POST /filters, PATCH/DELETE /filters/{id})
Functionsdeploy, update, redeploy, delete, tags, logs, tests, secrets, route binding, deployments, source, invocations, activity, usage and runtime keys. See Functions.
CredentialsPerson only. GET/POST /keys, GET/PATCH /keys/{id}, POST /keys/{id}/rotate, GET /keys/{id}/suggestion, GET /keys/builder, GET /credentials, POST /credentials/revoke, POST /credentials/revoke-all, GET /credential-events (JSON or CSV), GET/PATCH /credential-policy. Creating, rotating and changing a key accept a keys.create step-up.
Organization settingsPerson only. GET /me/grants, POST /me/grants/{id}/revoke (your own connected apps and CLI logins), GET /oauth-grants, POST /oauth-grants/{id}/revoke, GET/PATCH /organization, PUT/DELETE /organization/logo, GET/PUT /search-settings. Billing is in the dashboard. Name, logo and semantic search changes accept an org_settings.write step-up.
Webhook deliveriesGET /webhooks/deliveries, GET /webhooks/deliveries/{id}, POST /webhooks/deliveries/{id}/replay
InteractionsGET /interactions (filter by state, protocol, wire_id or the email_id of a carrier email), GET /interactions/{id} (with its steps), POST /interactions (start one), POST /interactions/{id}/steps (send the next step). Typed multi-step protocols carried over email; reading needs interactions:read and organization-wide mail scope.

For conceptual guides see Authentication, Search, Primitive Memories, Endpoints, Credits, Rate limits, and Routing.

Function-specific endpoints include:

GET    /v1/functions
POST   /v1/functions
GET    /v1/functions/{id}
PUT    /v1/functions/{id}
DELETE /v1/functions/{id}
POST   /v1/functions/{id}/redeploy
POST   /v1/functions/{id}/test
GET    /v1/functions/{id}/logs
GET    /v1/functions/{id}/invocations
GET    /v1/functions/{id}/secrets
POST   /v1/functions/{id}/secrets
PUT    /v1/functions/{id}/secrets/{key}
DELETE /v1/functions/{id}/secrets/{key}
PUT    /v1/functions/{id}/route

The full list, including deployments, usage, routing and runtime keys, is in Functions.

PUT /v1/functions/{id} replaces code and optionally accepts sourceMap for source-mapped logs.

OpenAPI

Fetch the machine-readable spec from:

curl https://api.primitive.dev/v1/openapi

The same document is also served as JSON at https://api.primitive.dev/v1/openapi.json; the YAML form lives at https://www.primitive.dev/openapi.yaml (requests to https://api.primitive.dev/v1/openapi.yaml redirect there). https://api.primitive.dev/v1/openapi is the canonical machine URL.

The full document covers every operation. To load only one area, fetch the per-tag index at https://api.primitive.dev/v1/openapi/tags. Each entry links a complete OpenAPI document for that tag's operations and the components they reference, for example https://api.primitive.dev/v1/openapi/tags/domains. The full document links the same index through its top-level x-tag-specs field and each tag's x-openapi-spec field.

Agents should inspect the OpenAPI document or use primitive describe <operation> before guessing request shapes.