SovrGPT Docs
API

Authentication

Bearer tokens, org scoping, security.

Every API call needs a bearer token in the Authorization header.

Authorization: Bearer sk-sovr-…

Creating an API key

  1. In the UI: Settings → API keys.
  2. Click "New API key" and give it a name.
  3. The token is shown in clear text once.
  4. Store it safely straight away (vault, environment variable, secret manager).

Important: keys are only ever stored server-side as a hash. If you lose a key you have to create a new one — the old one cannot be recovered.

Permissions (scopes)

When you create a key you choose which capabilities it may use — we scope by capability, not by individual model. All of them are enabled by default; narrow them down per use case (for example a TTS-only key with no chat access).

ScopeAllowsEndpoint
chatChat completionsPOST /v1/chat/completions
embeddingsEmbeddingsPOST /v1/embeddings
rerankRe-rankingPOST /v1/rerank
speechText-to-speechPOST /v1/audio/speech
transcribeSpeech-to-textPOST /v1/audio/transcriptions
mcpPlatform MCP toolsPOST /mcp/mcp

GET /v1/models and GET /v1/me need no particular scope — any valid key may read the catalogue and query its own permissions. If a key is missing the scope an endpoint needs, that endpoint answers 403 (permission_error, message key lacks <scope> scope). Existing keys keep full access; restricting is a deliberate choice at creation time.

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": "sovr_a1b2c3d4",
  "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 }
  ]
}

permissions lists every possible scope, not just the granted ones — otherwise you would have to know the full set of values in order to notice that one is missing. During setup it therefore says outright: "this key may transcribe, but it may not format."

The response never contains the key hash and never the identity of the person who created the key. key_prefix is the display value meant for this: it lets you recognise the key without disclosing it.

Org scoping

Every key belongs to an org. That means:

  • Usage is billed to the org, not to the individual user.
  • Logs appear under Org → Usage.
  • Removing someone from the org does not automatically disable their org-scoped keys — those keep working until an admin revokes them. → When offboarding, always revoke every key belonging to the departing user.

Revoking a key

Settings → API keys → "Revoke". Takes effect immediately. A revoked key returns this for every subsequent call:

HTTP/1.1 401 Unauthorized
{ "error": { "type": "invalid_request_error", "message": "invalid api key" } }

Error cases

Statuserror.typeMeaning
401invalid_request_errorHeader missing, key invalid, or key revoked.
403permission_errorThe key exists but lacks the scope this endpoint requires (key lacks <scope> scope).
429rate_limit_exceededRate limit reached. Respect the Retry-After header.
503service_unavailableModel endpoint is cold and starting up. Retry with backoff.

Best practices

  • Never commit keys. .gitignore must cover .env* and *.local.
  • Never ship keys to the client (browser, mobile app). Always go through a backend proxy.
  • Never put keys in URL query strings — they end up in server logs.
  • One key per use case. That way you can revoke precisely.
  • Rotate every 90 days.

CORS

The API is not meant for direct browser calls. If you try anyway, you get no Access-Control-Allow-* headers from us and the call fails. That is deliberate and protects you from accidentally leaking a key.

For browser apps: run your own backend proxy that keeps the keys server-side.

OAuth (planned)

For third-party apps that want to act on behalf of a SovrGPT user, an OAuth 2.0 authorization-code flow is planned for Q4/2026. Until then, API keys only.

Authentication