Authentication and API keys
SovrGPT API keys: create sov_… bearer tokens, restrict scopes per capability, query a key's permissions with GET /v1/me and revoke keys.
Every API call needs an API key as a bearer token in the Authorization header. Keys belong
to an organisation and can be restricted per capability.
Authorization: Bearer sov_…Creating an API key
- Open Settings → API keys.
- Enter a description, choose the scopes (all ticked by default) and click "+ New key".
- The key (
sov_followed by 48 hex characters) is shown in clear text once. Store it safely straight away (vault, environment variable, secret manager).
We only store a hash. A lost key cannot be recovered; create a new one instead.
Permissions (scopes)
Scopes restrict capabilities, not individual models. Create one key per use case, for example a text-to-speech-only key with no chat access.
| Scope | Allows | Endpoint |
|---|---|---|
chat | Chat completions | POST /v1/chat/completions |
embeddings | Embeddings | POST /v1/embeddings |
rerank | Re-ranking | POST /v1/rerank |
speech | Text-to-speech, designing your own voices | POST /v1/audio/speech, /v1/audio/voices/designs |
transcribe | Speech-to-text | POST /v1/audio/transcriptions, POST /v1/audio/uploads |
mcp | Platform MCP tools | POST /mcp/mcp |
decisions | Decision Engine | POST /v1/decisions |
No scope needed, only a valid key: GET /v1/models, GET /v1/me, GET /v1/audio/voices,
GET /v1/audio/voices/design-options, GET /v1/decisions/presets and
GET /v1/decisions/models.
If a key lacks the scope an endpoint needs, the endpoint answers 403 (permission_error,
message key lacks <scope> scope). Older keys that were never restricted have all seven
scopes.
Query the permissions instead of guessing
curl https://sovrgpt.com/api/v1/me -H "Authorization: Bearer $SOVR_KEY"{
"object": "api_key",
"id": "…",
"org_id": "…",
"name": "Dictation integration",
"key_prefix": "sov_a1b2c3d4",
"created_at": "2026-09-01T08:00:00.000Z",
"last_used_at": null,
"expires_at": null,
"scopes": ["transcribe"],
"permissions": [
{ "scope": "chat", "label": "Chat", "endpoint": "POST /v1/chat/completions", "granted": false },
{ "scope": "embeddings", "label": "Embeddings", "endpoint": "POST /v1/embeddings", "granted": false },
{ "scope": "rerank", "label": "Re-Ranking", "endpoint": "POST /v1/rerank", "granted": false },
{ "scope": "speech", "label": "Text-to-Speech", "endpoint": "POST /v1/audio/speech", "granted": false },
{ "scope": "transcribe", "label": "Speech-to-Text", "endpoint": "POST /v1/audio/transcriptions","granted": true },
{ "scope": "mcp", "label": "MCP-Tools", "endpoint": "POST /mcp/mcp", "granted": false },
{ "scope": "decisions", "label": "Decision Engine","endpoint": "POST /v1/decisions", "granted": false }
]
}permissions lists every scope, including the ones not granted. During setup you see at
once what is missing, for example: "this key may transcribe, but not chat".
expires_at: null means the key does not expire.
The response never contains the key hash and never the identity of the person who created
the key. key_prefix (the first twelve characters) lets you recognise the key without
disclosing it.
Keys and organisations
Every key belongs to an organisation:
- Usage is billed to the organisation, not to the individual. Per-person spend limits apply to the person who created the key.
- You can see usage under Settings → Usage.
- Keys of someone who leaves the organisation keep working until an admin revokes them. When offboarding, revoke every key belonging to that person.
Revoking a key
Settings → API keys → "Revoke". It takes effect immediately. Every further call with the key gets:
HTTP/1.1 401 Unauthorized
{ "error": { "type": "invalid_request_error", "message": "invalid api key" } }Error codes
| Status | error.type | Meaning |
|---|---|---|
| 401 | invalid_request_error | Header missing (missing bearer token), key invalid or revoked (invalid api key). |
| 402 | quota_exceeded | Spend limit reached; code is org_spend_limit or user_spend_limit. |
| 403 | permission_error | The key is valid but lacks the scope this endpoint requires (key lacks <scope> scope). |
| 429 | rate_limit_error | The model provider is throttling. Retry with backoff. For non-streaming chat, the provider's error message is passed through unchanged. |
| 503 | api_error (chat, code: model_cold_start), service_unavailable (audio) | The model is starting up. Retry with backoff; for chat, Retry-After gives the wait. |
Recommendations
- Never commit keys.
.gitignoreshould cover.env*and*.local. - Never ship keys to the client (browser, mobile app); go through your own backend.
- Never put keys in URL query strings; they end up in server logs.
- One key per use case, so you can revoke precisely.
- Rotate keys every 90 days.
CORS
The API is not meant for direct browser calls. It sends no Access-Control-Allow-* headers,
so such calls fail. That is deliberate and keeps keys out of browsers. For browser apps, run
your own backend proxy that holds the key server-side.
OAuth (planned)
For third-party apps that act on behalf of a person, an OAuth 2.0 authorization-code flow is planned for Q4/2026. Until then, API keys only.