- Header:
Authorization: Bearer <token> - Query parameter:
?api_key=<token>
Token precedence and fallback
When both anAuthorization header and an api_key query parameter are present, Sayna evaluates them in this order:
- The
Authorizationheader is evaluated first. - If the header format is invalid, the server falls back to the
api_keyquery parameter.
missing_auth_header.
Quick summary
Toggle auth with the
AUTH_REQUIRED environment variable. When it’s false, all endpoints accept unauthenticated requests.Option A — API secret authentication
Setup steps
-
Generate a long random secret.
-
Set the environment variables:
-
Clients send the secret as a bearer token in the header or as a query parameter:
Security notes
- Use at least 32 random bytes; rotate periodically and never commit secrets to VCS.
- Always terminate TLS before traffic reaches Sayna so the bearer token stays encrypted in transit.
- Pair with IP allowlists when you use this mode for back-office tools.
Option B — Delegated JWT authentication
API secret not enough? Let your own service validate tokens and return200/401 decisions.
1. Generate signing keys
2. Configure Sayna
3. Implement the auth service
Your service must:- Accept
POSTrequests whose body is the JWT Sayna generated. - Verify the JWT signature using the public key (RS256 or ES256).
- Validate the extracted bearer token and any contextual data.
- Return
200 OK(allow) or401 Unauthorized(deny). Other 4xx map to401, 5xx become502, and timeouts surface as503upstream.
Environment variables
When
AUTH_REQUIRED=true you must specify either AUTH_API_SECRET or both AUTH_SERVICE_URL and AUTH_SIGNING_KEY_PATH. You can configure both to let Sayna short-circuit known service-to-service traffic with the secret and route user traffic through the JWT flow.
JWT payload schema (delegated mode)
sub: Alwayssayna-authso your service knows who issued the JWT.iat/exp: Issued-at plus 5-minute expiration.auth_data.token: The extracted usable token, resolved from theAuthorizationheader or theapi_keyquery parameter (following the precedence rule above).auth_data.request_body: JSON payload of the HTTP request.auth_data.request_headers: Filtered headers (excludesauthorization,cookie,x-forwarded-*,x-sayna-*,host,x-real-ip).auth_data.request_path/request_method: Full request context for policy decisions.
Verification checklist
Protected endpoints
WhenAUTH_REQUIRED=true the following endpoints require authentication:
POST /speakGET /voicesPOST /livekit/tokenGET /livekit/roomsGET /livekit/rooms/{room_name}DELETE /livekit/participantPOST /livekit/participant/muteGET /recording/{stream_id}POST /sip/callGET /sip/hooksPOST /sip/hooksDELETE /sip/hooksPOST /sip/transfer
GET /(health)GET /ws(WebSocket handshake — see the WebSocket Auth roadmap below)
Room-scoped authorization
Beyond authentication, room-scoped endpoints enforce ownership viametadata.auth_id:
Room names are no longer prefixed or modified. Access control is enforced entirely through the
metadata.auth_id field stored in room metadata.WebSocket authentication status
WebSocket auth is not implemented yet. The open tasks from the design document:- Authenticate during the HTTP upgrade by reading query parameters or headers.
- Require an auth payload inside the first
configmessage. - Or leave
/wspublic but scope LiveKit mirroring to trusted rooms.
Usage examples
Client guidance
Recommended default: Use theAuthorization: Bearer <token> header whenever possible. Header-based auth keeps tokens out of URL strings, server logs, and browser history.
When to use query parameter auth: The ?api_key=<token> query parameter is practical in environments where adding custom headers is difficult — for example, browser redirects, webhook callbacks from third-party services, or server-sent event streams.
Backward compatibility
- Existing header-based clients continue to work unchanged.
- Query-based auth is additive and non-breaking.
- Clients currently sending an invalid
Authorizationheader alongside a validapi_keyquery parameter will now authenticate successfully via fallback.
Scope clarification
This dual-input auth behavior applies to protected HTTP endpoints only. It does not change:- The WebSocket auth model (see WebSocket authentication status below).
- The LiveKit webhook signature verification flow, which uses its own signing mechanism independent of bearer tokens.
Provider-level credentials are separate from server-level authentication. In addition to the API secret and JWT strategies described above, Sayna supports per-session and per-request provider credential overrides via the
auth field in STT/TTS config objects. This allows multi-tenant clients to supply their own Deepgram, ElevenLabs, Cartesia, Google, or Azure credentials without changing server configuration. See the WebSocket guide for details.Error responses
Mapping auth service responses
Security considerations
- Private keys: Store private keys with
chmod 600, never commit them, and rotate periodically. - Network security: Run Sayna + auth service over HTTPS/TLS. Consider mutual TLS between the two if you run them across trust zones.
- Token validation: Reject replayed JWTs by checking
iat; combine with short expirations. - Error handling: Return generic error bodies to clients but log detailed errors internally for auditing.
- Replay protection: Compare
iatagainstnow(±60 seconds) before trusting the payload.