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.
| Method | Header | Who signs it | Lifetime | Best for |
|---|---|---|---|---|
| RSA JWT (Bearer) | Authorization: Bearer <jwt> | Your backend, with your private RSA key | You choose (default 1 h) | Server-to-server calls |
| Access token | X-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
Authorizationbearer token wins.
A request with neither is rejected with 403 Forbidden.
Exception: the MCP endpoint. On
/mcp,Authorization: Bearercarries 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:
| Claim | Required | Meaning |
|---|---|---|
iss | yes | Issuer. Must be verbatim-ai.com. |
oid | yes | Your organization ID (UUID). Identifies the tenant. |
iat | yes | Issued-at (Unix seconds). |
exp | yes | Expiry (Unix seconds). Keep short. |
uid | no | End-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)
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 method Action GETreadPOSTcreatePUT/PATCHupdateDELETEdelete
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 JWT | Access token | |
|---|---|---|
| Scope claim absent / empty | Unrestricted (all domains, all actions) | Rejected — a scope is mandatory |
| Scope claim present | Restricted to the listed entries | Restricted 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
| Goal | Scope 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
Errorschema (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 →
Updated about 1 hour ago