SovrGPT Docs
API

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

  1. Open Settings → API keys.
  2. Enter a description, choose the scopes (all ticked by default) and click "+ New key".
  3. 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.

ScopeAllowsEndpoint
chatChat completionsPOST /v1/chat/completions
embeddingsEmbeddingsPOST /v1/embeddings
rerankRe-rankingPOST /v1/rerank
speechText-to-speech, designing your own voicesPOST /v1/audio/speech, /v1/audio/voices/designs
transcribeSpeech-to-textPOST /v1/audio/transcriptions, POST /v1/audio/uploads
mcpPlatform MCP toolsPOST /mcp/mcp
decisionsDecision EnginePOST /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

Statuserror.typeMeaning
401invalid_request_errorHeader missing (missing bearer token), key invalid or revoked (invalid api key).
402quota_exceededSpend limit reached; code is org_spend_limit or user_spend_limit.
403permission_errorThe key is valid but lacks the scope this endpoint requires (key lacks <scope> scope).
429rate_limit_errorThe model provider is throttling. Retry with backoff. For non-streaming chat, the provider's error message is passed through unchanged.
503api_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. .gitignore should 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.

Authentication and API keys