Skip to content

Authentication

Developer API keys: formats, the Bearer and x-api-key headers, scopes per route family, read-only keys, ceilings, rotation and hygiene.

Every API call is authenticated by a developer API key. A key belongs to one workspace, acts as the user who created it, and can be narrowed to the route families it needs, capped in questions per month and in requests per minute, made read-only, and given an expiry date. Narrowing is the point: the key you give to a client's application should be able to call the engine and nothing else.

Key format

A key looks like sk_live_ followed by 48 hexadecimal characters. Subsidia stores only a SHA-256 hash of it and a 16-character prefix (sk_live_xxxxxxxx) that the console shows to help you recognise it. The full key is returned once, at creation. There is no way to display it again; if it is lost, create a new key and revoke the old one.

Sending the key

HeaderUsed byNotes
Authorization: Bearer sk_live_...OpenAI SDKs, curl, most HTTP clientsThe value must start with sk_live_ or sk_test_. Any other Bearer value is not treated as an API key.
x-api-key: sk_live_...Anthropic SDKs, the Subsidia SDKTakes precedence over Authorization when both are present.
Same key, both styles
curl https://api.subsidia.protypa.fr/v1/models -H "Authorization: Bearer $SUBSIDIA_API_KEY"
curl https://api.subsidia.protypa.fr/v1/models -H "x-api-key: $SUBSIDIA_API_KEY"

Creating and managing keys

The simplest way is the Gateway page of the console (https://subsidia.protypa.fr/console/gateway). Keys are also managed through /developer/api-keys, which is authenticated by a signed-in user session and not by an API key: a key can never create, list, change or revoke keys, including itself.

Action Request Notes
Create POST /developer/api-keys Body: name, scopes, monthlyQuestionLimit, ratePerMinute, expiresAt (ISO date). Returns 201 with the full key once.
List GET /developer/api-keys Your active keys: id, name, prefix, scopes, ceilings, usageCount, lastUsedAt, createdAt, expiresAt. Never the secret.
Change PATCH /developer/api-keys/:id Any of name, scopes, monthlyQuestionLimit, ratePerMinute. null removes a limit. The secret and the expiry cannot be changed.
Revoke DELETE /developer/api-keys/:id 204. The key stops working immediately and cannot be reactivated.
Usage GET /developer/usage Request count and last use per key.

Limits must be positive whole numbers up to 10 000 000. A bad scopes list is a 400 that names the accepted values.

Create a restricted key
# $SESSION_TOKEN is the signed-in user's session token, not an API key
curl https://api.subsidia.protypa.fr/developer/api-keys \
-H "Authorization: Bearer $SESSION_TOKEN" \
-H "x-workspace-id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"name": "Client app - production",
"scopes": ["engine", "knowledge"],
"monthlyQuestionLimit": 2000,
"ratePerMinute": 60,
"expiresAt": "2027-06-30T00:00:00Z"
}'

Scopes

A scope grants a family of routes. Combine as many as the key needs. A restricted key that calls a route outside its families gets 403 scope_denied; a route that maps to no scope at all is refused for restricted keys too.

ScopeRoute familiesTypical holder
engine/v1/chat/completions, /v1/messages, /v1/messages/count_tokens, /v1/models, /v1/ai/*, /v1/proofs/*, /v1/gateway/*Any application that calls a model. See Gateway, Completions, Proof certificates.
knowledge/v1/knowledge-sources*, /v1/knowledge/*Anything that uploads or queries documents. See Knowledge sources.
proof/v1/verify*, /v1/registre*A checker or an audit tool. See Le Vérificateur and Le Registre. Exempt from the monthly question ceiling.
agents/v1/agents*, /v1/tools*Applications that manage or chat with agents. See Agents.
conversations/v1/conversations*Multi-agent debates. See Conversations.
webhooks/v1/webhooks*Event subscriptions. See Webhooks.
reflex/engine/*Automations. See Reflex.
light/light/*The Light assistant. See Light.
partner/partner/*Integrators managing client workspaces. See Integrators.
readonlynot a route familyA restriction, described below.

Read-only keys

Add readonly to the scopes and the key may only read. Any POST, PUT, PATCH or DELETE is refused with 403 read_only_key, with one deliberate exception: a few POST routes only ask a question and change nothing, so they stay open. These are /v1/chat/completions, /v1/messages, /v1/ai/complete, /v1/knowledge/query, /v1/verify, /light/ask and /light/web.

readonly is combined with capability scopes: ["engine", "readonly"] is a key that can ask the Gateway but cannot create anything. Inside the app a read-only key acts with the viewer role; every other key acts as the workspace owner.

Ceilings, limits and expiry

SettingEffectFailure
monthlyQuestionLimitCaps the questions this key may consume per calendar month (UTC). The count can lag a burst by a few seconds, so treat it as a guard rail against a runaway integration, not as a meter. An email alert is sent as the key approaches its ceiling.402 API_KEY_BUDGET_EXCEEDED
ratePerMinuteCaps requests per minute for this key, counted in the database so it holds across servers. Keys created without it have no per-key limit, but route-level limits still apply, see Rate limits.429 rate_limited with retry-after
expiresAtThe key stops working at that instant. Set one for any key handed to a project with an end date.401 API key expired

Rotating a key

  1. 1
    Create the replacement

    Create a new key with the same scopes and limits. Both keys are valid at the same time, so nothing breaks.

  2. 2
    Deploy it

    Roll the new value out to every place that uses the old one, through your secret manager.

  3. 3
    Check the old key is idle

    In the console or with GET /developer/usage, watch the old key's lastUsedAt stop moving.

  4. 4
    Revoke the old key

    Delete it. The next call made with it returns 401 Invalid or revoked API key.

Key hygiene

  • One key per application and per environment, named after both, so a leak points at its source.
  • Give each key the fewest scopes that work, a monthly ceiling, and an expiry when the project has an end.
  • Keep keys in server-side code only. A key shipped to a browser or a mobile app is public; put your own backend in front of the Gateway instead.
  • Never log the Authorization or x-api-key header. Log the key prefix if you need to tell keys apart.
  • Rotate on staff changes and after any suspected exposure; revoke first, investigate after.
  • The proof certificates and the usage journal record which workspace acted, so a revoked key leaves a clear trail.

Authentication errors

StatusCode or messageMeaningWhat to do
401API key required. Pass x-api-key header or Authorization: Bearer sk_live_xxxNo usable key was sent.Check the header name and that the value starts with sk_live_.
401Invalid or revoked API keyThe key is unknown or was revoked.Create a new key. A typo and a revoked key look the same on purpose.
401API key expiredexpiresAt has passed.Create a replacement key.
403scope_deniedThe key lacks the scope for this route, or the route has no scope.Add the scope named in the message, or use another key.
403read_only_keyA read-only key tried to change something.Use a key without readonly for writes.
403client_suspendedThe workspace was suspended by its integrator.Contact your provider. Keys are kept and work again when access is restored.
403provider_unpaidThe integrator that hosts this client workspace has an overdue invoice.Contact your provider. Nothing is deleted.
402API_KEY_BUDGET_EXCEEDEDThe key reached its monthly question ceiling.Raise the ceiling with PATCH /developer/api-keys/:id, or wait for the next month.
429rate_limitedMore requests per minute than the key allows.Wait for retry-after seconds.

The full catalogue, including the OpenAI and Anthropic error envelopes, is in Errors.

Related