[{"data":1,"prerenderedAt":4},["ShallowReactive",2],{"article-doc:docs\u002Fstart\u002F3.authentication":3},"---\ntitle: Authentication\ndescription: \"API key, session token, and browser-side security.\"\n---\nAll API requests require an API key. This page explains the key format, usage patterns, and browser-side security model.\n\n## API key format\n\n- Prefix: `sk-`\n- The plaintext key is returned only once at provisioning. The server stores only a SHA-256 hash.\n- Minimum length: 16 characters (at least 13 characters after `sk-`)\n\n## Server-side usage\n\nAll endpoints accept the key via the `Authorization: Bearer` header:\n\n```bash\ncurl -H \"Authorization: Bearer sk-...\" https:\u002F\u002Faudio.lansonai.com\u002Fv1\u002Faudio\u002Ftranscriptions\n```\n\nThis works for both offline HTTP requests and WebSocket upgrade handshakes.\n\n## Browser-side security\n\n::callout{icon=\"i-lucide-circle-alert\" color=\"red\"}\nNever expose your long-lived API key in frontend code or WebSocket URLs. Use a short-lived session token for browser connections.\n::\n\n### Session token flow\n\nBrowsers cannot safely store a long-lived key. LansonAI provides a short-lived token mechanism:\n\n```\n1. Frontend → your backend: request a session token\n2. Your backend → LansonAI:\n   POST \u002Fv1\u002Faudio\u002Ftranscriptions\u002Fsession-token\n   Authorization: Bearer sk-...   (your long-lived key, server-side only)\n3. LansonAI → your backend: returns rt_... token (valid 60 seconds)\n4. Your backend → frontend: pass the rt_... token\n5. Frontend → LansonAI: open WebSocket\n   ?access_token=rt_...\n```\n\n### Session token details\n\n| Property | Value |\n|---|---|\n| Prefix | `rt_` |\n| Validity | 60 seconds |\n| Signature | HMAC-SHA256 |\n| Signing secret | `GATEWAY_SESSION_TOKEN_SECRET` (server-side config) |\n| Transport | URL query `?access_token=rt_...` or `?token=rt_...` |\n\n### Token response\n\n```json\n{\n  \"token\": \"rt_eyJ...\",\n  \"expires_in\": 60,\n  \"endpoint\": \"\u002Fv1\u002Faudio\u002Ftranscriptions\u002Fstream\"\n}\n```\n\nIf `GATEWAY_SESSION_TOKEN_SECRET` is not configured, the endpoint returns `503 token_issuer_unavailable`.\n\n## WebSocket authentication methods\n\nThe realtime WebSocket accepts three authentication methods:\n\n| Method | Use case | Usage |\n|---|---|---|\n| `Authorization: Bearer sk-...` | Server-side clients | HTTP upgrade header |\n| `?access_token=rt_...` | Browser | URL query parameter |\n| `?token=rt_...` | Browser | URL query parameter (alias) |\n\n::callout{icon=\"i-lucide-triangle-alert\" color=\"amber\"}\nNever put `sk-...` in a URL. Browser connections must always use `rt_...` session tokens.\n::\n\n## Admin API\n\nKey provisioning, revocation, and plan-catalog endpoints exist under `\u002Fv1\u002Fexternal-transcription`, but they are for internal operators and dashboards, not regular API consumers. See [Admin API](\u002Fdocs\u002Fapi-reference\u002Fadmin) for details.\n\n## Error handling\n\n| Error code | HTTP | Cause |\n|---|---|---|\n| `missing_api_key` | 401 | No Authorization header or query token |\n| `invalid_api_key` | 401 | Key format invalid or unknown key |\n| `revoked_api_key` | 401 | Key has been revoked |\n| `expired_api_key` | 401 | Key has expired |\n| `auth_unavailable` | 503 | Key store unavailable |\n| `invalid_session_token` | 401 | Session token invalid or expired |\n| `token_issuer_unavailable` | 503 | Token issuer not configured |\n\nSee [Errors](\u002Fdocs\u002Fapi-reference\u002Ferrors) for the full error reference.\n\n## Next steps\n\n- [Quickstart](\u002Fdocs\u002Fstart\u002Fquickstart) — first request\n- [Errors](\u002Fdocs\u002Fapi-reference\u002Ferrors) — full error reference\n- [Realtime API](\u002Fdocs\u002Fapi-reference\u002Frealtime-api) — WebSocket endpoint details\n",1790059118962]