Token-Based Authentication
Token-Based Authentication
Anyone can extract an API key from a browser or mobile app. Keep the key on your server, mint a short-lived access token from it with POST /waves/v1/auth/token, and hand the token to the client. The client can use that token for TTS, STT and speech-to-speech inference, plus voice listing. It cannot mint tokens, clone voices, or read analytics, and it expires after 15 minutes at most.
Server-side code keeps using the API key directly. These tokens are for TTS, STT and speech-to-speech model inference. Voice-agent browser sessions use a different token, described in the Browser Voice Cookbook.
How it works
Mint a token on your server
Call POST /waves/v1/auth/token with your API key. The body is optional. ttl_seconds sets how long the token lives, from 30 to 900 seconds. The default is 300.
The response is 201 Created:
Only an API key can mint a token. A request that carries a token instead returns 403.
Hand the token to your client
Return access_token and expires_at to the client over your own authenticated channel, for example the session endpoint the client already calls. A token works any number of times until it expires, including for concurrent requests. Tokens cannot be renewed or extended, so mint a new one shortly before it expires. Compute the expiry on the client from expires_in, so a wrong client clock does not matter, and keep a small margin:
Expired tokens return 401. Browser code can read that status, since error responses carry CORS headers. Mint a new token and retry.
Call the API with the token
Use the token exactly like an API key, in the Authorization header.
WebSocket connections accept the token either in the Authorization: Bearer header or as the api_key query parameter. The query parameter works on WebSocket connections only. HTTP requests must use the header. Use the header when your client can set one, since query strings can end up in access logs.
A WebSocket that is already open keeps working after its token expires. The token is checked when the connection is made. Opening a new connection needs a token that is still valid.
What a token can call
Tokens work on TTS, STT and speech-to-speech inference, both the unified routes and the dedicated Lightning v3.1 routes, plus voice listing. Treat any route not listed here as unavailable to tokens.
The older POST /waves/v1/pulse/get_text path accepts API keys but rejects short-lived tokens. Client code that authenticates with a token must call the unified POST /waves/v1/stt/ endpoint.
Token lifecycle
- A token expires at
expires_at. Requests after that return401with{"error": "Invalid or expired access token"}. Mint a new token; there is no refresh. - Deleting the API key that minted a token invalidates the token shortly afterwards. It is not instant.
- A token is valid only in the region that minted it.
api.smallest.airoutes each request to the nearest region, so a token minted by a server in one region is rejected with401when a client in another region uses it. If your server and your users can be in different regions, mint and call through the same region-pinned hostname:api.india.smallest.ai(Mumbai) orapi.us.smallest.ai(Oregon). - Requests made with a token are billed to the API key that minted it. Plan, rate limits, and concurrency are unchanged.
- The mint endpoint is rate limited and returns
RateLimit-Limit,RateLimit-Remaining, andRateLimit-Resetheaders. Mint one token per client session, not one per request.
Token errors
For the full request and response schema, see Create Access Token.
End-to-end example
A complete flow in two files. The server holds the key and exposes one route. On a button click, the page fetches a token, plays synthesized speech, and opens an STT WebSocket ready for microphone audio.
Run SMALLEST_API_KEY=... node server.mjs and open http://localhost:3000. The API key never reaches the page.

