Authentication
The bearer credential every request carries: issued keys, scopes, expiry, revocation, and the browser WebSocket seam.
The credential
The API accepts a bearer credential in the Authorization header. The server compares the whole header in constant time. A missing or wrong credential is 401.
Authorization: Bearer <token>Issued keys
Tokens have the fixed form srk_<16>_<32>: a 16-character key id and a 32-character secret, both drawn from the alphabet abcdefghijklmnopqrstuvwxyz234567.
srk_aaaaaaaaaaaaaaaa_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaThe plaintext token is shown exactly once at issuance and is never recoverable. If it is lost, issue a new one and revoke the old. The server stores only a salted SHA-256 verifier.
Scopes
A key carries scopes. An empty scope set means unrestricted; that is the deliberate default, so keys issued before scopes existed keep working. The two scopes in use:
asr— creating a session, streaming to it, and transcribing.admin— metadata, metrics and drain.
A valid credential presented for an operation outside its scopes is 403.
401 versus 403
401 we do not accept this credential missing, wrong or expired
403 we accept it but it may not do this valid, but outside its scopesA credential that is missing, wrong or expired is 401. The same status meets a key whose expiry has passed. A valid credential that asks for an operation its scopes do not cover is 403.
Expiry
A key may carry an expiry. Expiry is checked on every verification against a fresh clock, so an expired key stops working within the verification cache window rather than at some cached moment. An expired key is 401.
Revocation
Revocation is not instant. A revoked key stops working within the verification cache TTL plus storage propagation, not at the moment it is revoked.
Browser credentials
A browser cannot set an Authorization header on a WebSocket. Both WebSocket routes therefore also accept the credential inside the Sec-WebSocket-Protocol header, as bearer.<token> alongside the required dicon.v1:
const socket = new WebSocket(url, ["dicon.v1", `bearer.${token}`]);When both forms are present, the Authorization header wins. The server selects the dicon.v1 subprotocol and never echoes the credential back.
The cost, stated plainly: this is a handshake request header. It is better than putting the token in a URL, but an intermediary that logs full request headers will capture it.
Handshake failures
A 401 on a WebSocket upgrade is a real HTTP response; the connection never opens. Your client should handle it as an authentication failure.