Skip to main content
POST
Create an access token
Exchanges your API key for a short-lived, single-use access token (fin_tok_…). Your server calls this endpoint, hands the token to the browser, and the browser uses it to open the Realtime WebSocket or call speak / stream directly. The API key itself never leaves your server.
  • Short-lived - expires after ttl_seconds (default 300, max 3600).
  • Single use - consumed on its first successful use: one WebSocket handshake, or one speak / stream request. A request refused before it runs (a 429 rate limit, a plan check) keeps the token, so you can retry with it. Mint a new token for each session or request.
  • Text-to-speech only - a token grants the tts scope and nothing else. It can’t list, create or clone voices.
  • Optionally voice-restricted - pass voice_ids to limit which voices the token can synthesize with.
  • Tied to its key - it uses the creating key’s workspace and stops working if that key is revoked or expires.

Authorization

string
required
Your workspace API key (flv_…). Required scope: tts.

Body

All fields are optional; an empty body (or {}) mints a 5-minute token for any voice.
integer
default:300
Token lifetime in seconds, 10–3600. Out of range returns 400.
string[]
Restrict the token to these voices. Each id must be a voice your key’s workspace can use, otherwise the request returns 400 naming the bad id. Omit to allow any voice in the workspace. Using the token with another voice returns voice_not_allowed.

Response

string
The access token (fin_tok_…). Shown only in this response - the server stores a hash.
integer
When the token expires, as Unix epoch milliseconds.
integer
The effective lifetime in seconds.
Response

Browser flow

1

Your server mints a token

When a signed-in user needs audio, your backend calls POST /tokens with your API key (scope tts), optionally restricting voice_ids to what that user may use.
2

Hand the token to the browser

Return only the token (and expires_at) to your frontend. Never send the API key.
3

The browser uses it once

Open the WebSocket with the token as a subprotocol, or call speak / stream with Authorization: Bearer <token>. The token is consumed on that first successful use.
If your WebSocket client can’t send subprotocols, pass the token as ?access_token=fin_tok_… on the stream URL instead. The older ?api_key= query parameter still works but is deprecated - it puts your long-lived key in URLs and browser history.

Errors

When the browser uses the token: an unknown, expired or already-used token returns 401 token_invalid (on the WebSocket, the handshake fails with 401); a voice outside voice_ids returns 403 voice_not_allowed on HTTP, or a non-fatal voice_not_allowed error frame on the WebSocket. See Errors.

Prompt

Copy this into your coding agent (Claude Code, Cursor, Codex…) to implement this endpoint.