Authentication

Every request to a /v1/ endpoint must be authenticated.

Every request to a /v1/ endpoint must be authenticated. The Verbatim AI API accepts
two credential types, and you pick one per request by sending the matching header.

MethodHeaderWho signs itLifetimeBest for
RSA JWT (Bearer)Authorization: Bearer <jwt>Your backend, with your private RSA keyYou choose (default 1 h)Server-to-server calls
Access tokenX-Access-Token: <token>Verbatim (minted from a JWT call)Short (default 1 h)Browser / widget / untrusted clients

One header per request. If both are present, the Authorization bearer token wins.
A request with neither is rejected with 403 Forbidden.

Exception: the MCP endpoint. On /mcp, Authorization: Bearer carries an access token,
never a JWT, and a request without a valid one is rejected with 401. See MCP server.

Choose based on where the code runs:

  • On your servers → use an RSA JWT. Your private key never leaves your
    infrastructure.
  • In a browser or any client you don't fully trust → never ship the private key. Mint a
    short-lived, scoped access token on your backend and hand that to the
    client.

1. RSA JWT (Bearer token)

You generate an RSA key pair, register the public key with Verbatim (via the
backoffice), and sign JWTs with the private key. The server verifies the signature with
your published public key and trusts the claims inside.

Header sent on every request:

Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVC...

The JWT itself:

  • Algorithm: RS512 (RSA signature with SHA-512).
  • Header must include kid — the UUID of your registered public key (the key id).
  • Payload claims:
ClaimRequiredMeaning
issyesIssuer. Must be verbatim-ai.com.
oidyesYour organization ID (UUID). Identifies the tenant.
iatyesIssued-at (Unix seconds).
expyesExpiry (Unix seconds). Keep short.
uidnoEnd-user ID, echoed back by GET /v1/auth/whoami.

A JWT with no scope claim is unrestricted — it can call every domain and every HTTP
method allowed for your organization. See Scopes below.

Full instructions — generating keys, publishing the public key, and minting tokens — are in
the RSA keys user guide.


2. Access token

An access token is a short-lived, opaque string minted by
POST /v1/auth/access-token. That call must itself be authenticated with an RSA JWT, so the
flow is always: backend holds the JWT → backend mints an access token → untrusted client
uses the access token.

Header sent on every request:

X-Access-Token: 8Jf3kQ...

Unlike a JWT, an access token must be created with an explicit, non-empty scope, so it
is always least-privilege, and it lives at most 24 hours (the platform ceiling on ttl). Its
value is returned once, at creation; GET /v1/auth/access-token lists your tokens with their
values masked. Everything about creating, listing, scoping, using and revoking access tokens is
in the Access keys guide.


Scopes (DOMAIN:ACTION)

Scopes restrict what a token can do. A scope is a string DOMAIN:ACTION:

  • DOMAIN — one of config, auth, session, doc, chunk, corpus, post, usage, agent.
    It matches the first path segment after /v1/ (e.g. /v1/doc/... → doc).

  • ACTION — derived from the HTTP method:

    HTTP methodAction
    GETread
    POSTcreate
    PUT / PATCHupdate
    DELETEdelete

For a request to be authorized, the token's scope list must contain the exact
DOMAIN:ACTION entry for that request. For example, POST /v1/doc/init requires
doc:create; GET /v1/corpus/ requires corpus:read.

The two credential types treat scope differently:

RSA JWTAccess token
Scope claim absent / emptyUnrestricted (all domains, all actions)Rejected — a scope is mandatory
Scope claim presentRestricted to the listed entriesRestricted to the listed entries

So an unscoped RSA JWT is the "admin" credential; a scoped access token is the "least
privilege" credential you hand to clients.

Examples

GoalScope list
Read-only widget that can query["corpus:read", "session:read", "post:read", "post:create"]
Uploader service["doc:create", "doc:read"]
Read corpora and list documents["corpus:read", "doc:read"]

What "onboarded" means

The server only trusts a token whose oid (organization) resolves to a real organization.
A syntactically valid JWT signed by an unknown key, with a wrong issuer, or without oid,
is rejected. Use GET /v1/auth/whoami to confirm a token resolves to the identity you
expect.


Failure behavior

  • Missing/invalid/expired credentials → 403 Forbidden.
  • Valid credentials but the scope doesn't cover the request → 403 Forbidden.
  • The error body follows the standard Error schema (see
    API conventions).

Note the API returns 403 (not 401) for authentication failures — there is no
challenge/WWW-Authenticate flow; you either present a valid credential or you are
forbidden.

Next: RSA keys user guide → · Access keys → ·
Security best practices →


Did this page help you?