Skip to main content

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.

Every request to the Titan API must include a valid API key. Titan uses binary API keys with a titan_ prefix — they are cryptographically self-contained, verified without a database lookup, and carry the scopes and expiry directly inside the token. This page covers everything you need to create, scope, rotate, and troubleshoot your keys.

Key format

API keys follow this format:
titan_AAAB...  (~104 characters total)
The key consists of the titan_ prefix followed by a base64url-encoded binary payload. The payload contains the key ID, tenant ID, scope bitmask, expiry timestamp, and an HMAC-SHA256 signature. The HMAC is never exposed in API responses.
If you are migrating from an older integration, legacy hex-format keys still work but emit a deprecation warning. Rotate to the new format at your earliest convenience.

How to authenticate

Pass your API key in the Authorization header as a Bearer token on every request:
curl --request GET \
  --url https://api.example.com/api/sessions \
  --header 'Authorization: Bearer titan_AAAB...'
There is no session cookie, no OAuth exchange, and no token refresh — just the key in every request.

Creating a key

Use POST /api/account/keys to create a new key. You must specify a human-readable label, a lifetime in days, and the scopes you want to grant.
curl --request POST \
  --url https://api.example.com/api/account/keys \
  --header 'Authorization: Bearer titan_...' \
  --header 'Content-Type: application/json' \
  --data '{
    "label": "production-bot",
    "lifetimeDays": 30,
    "scopes": ["sessions:manage", "messages:write", "webhooks:manage"]
  }'
Response:
{
  "data": {
    "id": "key_01jz...",
    "key": "titan_AAAB...",
    "label": "production-bot",
    "scopes": ["sessions:manage", "messages:write", "webhooks:manage"],
    "createdAt": "2025-05-01T12:00:00Z",
    "expiresAt": "2025-05-31T12:00:00Z"
  }
}
The full key value is only returned in the creation response. Store it securely — you cannot retrieve it again. If you lose it, rotate the key to get a new one.
Key properties you can update later via PATCH /api/account/keys/{id}:
  • label — rename the key at any time.
  • isActive — set to false to disable the key without deleting it.

Scopes

Scopes limit what a key can do. Always grant the minimum scopes your integration needs.
ScopeWhat it allows
*All permissions — use only for testing
sessions:readRead session status and info
sessions:manageCreate, start, stop, pair, and delete sessions
messages:writeSend messages
chats:readList chats and chat metadata
chats:manageArchive, mute, and manage chats
contacts:readRead contacts and check if numbers are on WhatsApp
contacts:manageBlock and unblock contacts
groups:readRead group info and participant lists
groups:manageCreate groups, change settings, manage participants
channels:readList and read channel (newsletter) info
channels:manageCreate, follow, mute, and delete channels
status:writePost WhatsApp Status (stories)
presence:readRead contact presence
presence:writeSet your own presence (online/offline)
labels:readRead WhatsApp Labels
labels:manageCreate, update, and assign labels to chats
profile:readRead your own profile info
profile:writeUpdate your display name, status text, and profile picture
webhooks:manageCreate, update, and delete webhook subscriptions
media:readDownload and decrypt media files
media:manageConfigure media storage settings

Key expiry

All keys have a mandatory expiry. The default maximum lifetime is 90 days, though your plan may configure a different maximum. The expiresAt field in the key response tells you exactly when the key will stop working. Plan for key rotation before expiry. If a key expires during active use, all requests using it will start returning 401 Unauthorized.

Rotating a key

Key rotation atomically creates a new key and immediately revokes the old one. Use POST /api/account/keys/{id}/rotate:
curl --request POST \
  --url https://api.example.com/api/account/keys/key_01jz.../rotate \
  --header 'Authorization: Bearer titan_...'
Response:
{
  "data": {
    "id": "key_02ab...",
    "key": "titan_BBBC...",
    "label": "production-bot",
    "scopes": ["sessions:manage", "messages:write", "webhooks:manage"],
    "createdAt": "2025-05-15T09:00:00Z",
    "expiresAt": "2025-06-14T09:00:00Z"
  }
}
The old key is revoked immediately across all API pods — there is no grace period. Update your application to use the new key before calling rotate.

Error responses

StatusMeaning
401 UnauthorizedThe key is missing, malformed, expired, or has been revoked.
403 ForbiddenThe key is valid but does not have the required scope for the endpoint.
Both errors return a JSON body with an error field describing the problem:
{ "error": "api key expired" }
{ "error": "insufficient scope: messages:write required" }

Client tokens

For browser-based integrations where you cannot safely expose an API key, Titan provides client tokens (titan_ct_ prefix). Client tokens are short-lived, carry no authority themselves, and have their permissions enforced server-side. They are intended for end-user-facing flows such as the embedded connect widget. See Client Tokens for full documentation on minting tokens, configuring rules, and valid actions.

Self-hosted edition

On the self-hosted edition, the API server also accepts a static master key on admin routes (/admin/*). The master key is set during deployment and is separate from the per-tenant API key system described on this page. Use the master key only for instance administration — for everything else, create a regular API key.
The master key grants unrestricted access to all admin endpoints. Treat it with the same care as a root password and rotate it by redeploying with a new value.