Client tokens let you give a browser or mobile client direct access to a subset of the Titan API without putting your server API key in client-side code. You mint a token on your server, pass it to the browser, and the browser uses it exactly like a normal API key — but Titan enforces strict server-side rules that limit what the token can do, who it can send to, and how many requests it can make.Documentation Index
Fetch the complete documentation index at: https://docs.usetitan.app/llms.txt
Use this file to discover all available pages before exploring further.
How client tokens work
A client token is a short-lived JWT signed with HMAC-SHA256. It has the prefixtitan_ct_ and a default lifetime of 15 minutes (900 seconds). The token itself carries zero authority — all permissions are defined by rules you set on the server and enforced by Titan on every request.
The flow has two sides:
- Server side: you configure rules for a session and mint a token against it.
- Browser side: the browser uses the token in the
Authorizationheader. Titan validates the token, loads the rules from its store, and enforces them on every call.
Client tokens can only access a single named session. A token minted for session
default cannot reach session support.Step 1: Configure rules for your session
Before minting any tokens, set the rules that govern what client tokens are allowed to do on a session. Rules are stored server-side and take effect immediately — you can update them at any time without reissuing tokens.Rules fields
Controls who client tokens can send messages to. See Recipient modes below.
Comma-separated list of actions the token may perform. If omitted, no actions are allowed. See Allowed actions below.
Maximum requests per minute per ephemeral ID (sliding window). Set to
0 for no limit.Maximum send actions per day per ephemeral ID. Only applies to
send_message and send_reaction. Set to 0 for no limit.Comma-separated list of allowed CORS origins. Leave empty to skip the origin check.
Whether client tokens are accepted for this session. Set to
false to immediately deny all existing tokens.Step 2: Mint a token on your server
Once rules are in place, mint a token from your backend for each browser session or tab. Pass anephemeralId — any string that uniquely identifies this client, such as a browser fingerprint, user ID plus tab ID, or UUID. Titan uses it for rate limiting and correlation.
Request fields
The name of the WhatsApp session this token is scoped to.
Your identifier for this client — used for per-minute and daily rate limiting.
Token lifetime in seconds. Defaults to
900 (15 minutes). Maximum is configurable server-side.token value to the browser. The expiresAt timestamp tells you when to request a new token.
Step 3: Use the token in the browser
The browser uses the client token in theAuthorization header exactly like a server API key:
SDK examples
All four SDKs accept either a server API key or a client token — you initialize the client the same way.Recipient modes
TherecipientMode rule controls who client tokens can send messages to. It applies only to send_message and send_reaction.
| Mode | Behavior | Typical use case |
|---|---|---|
none | Sending is disabled. Only read actions work. | Read-only dashboards |
conversation | Can only message JIDs that have already messaged the session first. | Support widgets, reply-only bots |
any | Can message any JID. | Internal tools with trusted browser clients |
verified | Reserved for future use. | — |
How conversation mode works
When a message arrives on the session, Titan records the sender’s JID. When a client token tries to send a message, Titan checks whether the target JID is in that set. If not, the request is denied with 403 Forbidden. This prevents client tokens from initiating new conversations — they can only reply to people who reached out first.
Allowed actions
TheallowedActions rule is a comma-separated list that determines which routes a client token can access. All other routes are always denied, regardless of rules.
| Action | Routes |
|---|---|
send_message | POST /:session/messages/send — send text, image, video, document, audio, sticker, poll, location, contact |
send_reaction | POST /:session/messages/react — react to a message with an emoji |
send_typing | POST /:session/messages/typing — send a typing indicator |
send_seen | POST /:session/messages/seen — mark messages as read |
read_presence | GET /:session/presence, GET /:session/presence/:chatId — query online status |
subscribe_presence | POST /:session/presence/:chatId/subscribe — subscribe to presence updates |
read_contact | GET /:session/contacts and related endpoints — query contact info and profile pictures |
Rate limiting
Two independent limits are applied perephemeralId:
Per-minute sliding window — when rateLimit is set (for example, 5), each ephemeral ID can make at most 5 requests per rolling 60-second window. Applies to all actions.
Daily cap — when maxDaily is set (for example, 100), each ephemeral ID can perform at most 100 send actions per 24-hour period. Only applies to send_message and send_reaction.
Both limits return 429 Too Many Requests when exceeded.
Error responses
| Status | When it occurs |
|---|---|
400 Bad Request | Invalid request body, missing required fields, TTL out of range, or invalid recipientMode |
401 Unauthorized | Token is invalid, expired, or has a bad signature; or no rules are configured for the session |
403 Forbidden | Route is not accessible to client tokens; session mismatch; action not in allowedActions; recipient not in conversation set; or origin not in allowedOrigins |
429 Too Many Requests | Per-minute rate limit or daily cap exceeded |