Skip to main content

Authentication

Every request carries one header, whatever the credential behind it:

Authorization: Bearer <credential>

The API tells the credentials apart by their shape, so there is nothing else to declare.

Credential types​

CredentialWho holds itHow to get oneLifetime
API key (nat_sk_…)Your backend, CI, a scriptPOST /v1/api-keys while signed in — see API KeysUntil rotated or revoked
Session token (JWT)A person in the app or the CLISign in with a code emailed to the address — see Auth15 minutes; refresh for 30 days
OAuth access token (JWT)An MCP client such as Claude or CursorThe client runs OAuth 2.1 and you approve it — see MCP Server1 hour; the client refreshes for 30 days

Use an API key for anything that runs without a person in front of it, and scope it to one project unless it genuinely needs the whole account. A session token is what the app holds; never store one where an API key would do. An OAuth token is minted for and held by the client that asked for it — you never copy it anywhere.

The raw nat_sk_… secret is returned once, at creation or rotation. A key cannot be minted by an OAuth token: creating one is a human act.

What a credential reaches​

A request acts in the project named in its path, /v1/projects/{project_id}/…. Two independent checks decide whether it may:

  1. Credential scope. A project-scoped API key reaches its one project. An OAuth token reaches the projects chosen on its consent screen. An account-scoped key and a session token reach every project the user belongs to.
  2. Membership. The user behind the credential must be a member of that project, with a role wide enough for the action. member reads and writes every resource in the project; admin also manages who is in it and how it is configured; owner also deletes it. See Projects → Membership.

Scope narrows membership; it never widens it. A project-scoped key whose user is removed from the project stops reaching it.

Credentials are resolved on every request, not trusted from what a token claims. Revoking a key, disconnecting an app or changing a member's role takes effect on the next call, not when a token expires.

When a request is refused​

StatuscodeMeans
401unauthorizedNo Authorization header, a malformed one, or a credential that is unknown, expired or revoked. Refresh a session; replace a key.
404not_foundThe user is not a member of the project. A project you cannot reach looks the same as one that does not exist.
403access_deniedThe credential's scope excludes the project, or the role is too narrow — the message names the role the action needs.

401 is about the credential and 403 about what it may do. Sign-in endpoints also answer 429; see Rate limits.

Check which account a credential resolves to​

GET /v1/users/me answers for every credential type, which makes it the first call to try with a new one.

export NATURALI_TOKEN=nat_sk_...
naturali get-current-user