Authentication
API keys for the control plane, short-lived WebSocket tokens for the media stream — what to use where, and how to handle both safely.
Every Orvyn API surface is authenticated, with two different credential types for two different planes:
| Credential | Where it is used | Lifetime |
|---|---|---|
| API key | All six /v1/ REST endpoints (headers) |
Long-lived, admin-managed |
| WebSocket token | The media WebSocket (?token= in ws_url) |
Short-lived, 5 minutes |
API key authentication
All /v1/ routes accept your API key via either header:
Authorization: Bearer <api_key>x-api-key: <api_key>Where keys come from
API keys are created and revoked through the admin surface of the API itself:
POST /v1/api-keys— create a key. The plaintext is shown exactly once at creation; Orvyn stores only a SHA-256 hash.DELETE /v1/api-keys/{key_id}— revoke a key. Revocation takes effect immediately; keys do not expire on their own.
Auth errors
| Status | error.code |
Meaning |
|---|---|---|
| 401 | missing_api_key |
No key provided |
| 403 | invalid_api_key |
Key not recognized or revoked |
| 503 | api_auth_not_configured |
Server has no keys configured |
WebSocket token authentication
The media WebSocket (GET /v1/sessions/{session_id}/ws) is not
API-key authenticated. It uses a short-lived HMAC-signed token embedded in the
connection URL:
wss://<orvyn-public-api-host>/v1/sessions/{session_id}/ws?token=...
Token properties
| Property | Value |
|---|---|
| Signing | HMAC-SHA256 |
| TTL | 5 minutes (300 seconds) |
| Payload | Session id, tenant id, purpose — never the raw API key |
| Binding | Bound to one session id and one tenant |
The token is the authorization: anyone holding a valid token can connect to that session’s WebSocket. This is by design, but it means the URL must be handled like a credential.
Token lifecycle
You receive the first token in the POST /v1/sessions response (stream.ws_url
includes it). For reconnects after the 5-minute expiry, mint a fresh one:
curl -X POST \
"https://<orvyn-public-api-host>/v1/sessions/$SESSION_ID/client-token" \
-H "Authorization: Bearer $AVTR_API_KEY"
{
"success": true,
"data": {
"ws_url": "wss://<orvyn-public-api-host>/v1/sessions/session:.../ws?token=eyJ...",
"token_expires_at": 1718352600000
},
"timings": { "latency_ms": 3 }
}
What to never put in a URL
| Never in a URL | Use instead |
|---|---|
API keys (Authorization values) |
Headers on REST calls |
| Long-lived provider credentials | provider_config in POST /v1/sessions, minted per session by your backend |
The full ws_url in logs/analytics |
Open the connection, then discard the URL |
Request correlation
Every /v1/ response includes an X-Request-Id header for tracing. You may
supply your own (1–128 characters of [a-zA-Z0-9_-]); it is echoed back when
valid, otherwise a fresh id is generated. The id also appears in the timings
object of JSON responses.
Request shape contract
Every response uses one envelope:
{
"success": true,
"data": {},
"timings": { "latency_ms": 123 }
}
Errors keep the same shape with "success": false and an error object
carrying a stable code — see
errors, limits & security.