Authentication
Every request sends one credential as Authorization: Bearer <credential>.
Which credential an endpoint takes is fixed; each endpoint's page says
which. A missing header returns 401 missing_authorization. A header
that is not Bearer <something> returns 401 malformed_authorization.
API keys
For your backend only. Never ship an API key to a browser or an app.
Authorization: Bearer pk_live_<prefix>_<secret>
Keys are created in the dashboard (see API keys).
A key looks like pk_live_… or, for a test key, pk_test_…. The full
key is shown once, when you create it. Afterwards only its prefix is
visible. If you lose a key, revoke it and create a new one.
| Failure | Response |
|---|---|
| Key unknown, malformed, or wrong secret | 401 invalid_api_key |
| Key revoked | 401 revoked_api_key |
| Key lacks the scope the endpoint needs | 403 forbidden. The message names the missing scope. |
Scopes
A key carries a list of scopes, chosen when you create it. Endpoints not listed here need no scope: any valid key works.
| Scope | Unlocks |
|---|---|
users:manage | POST /v1/users/:user_id/tokens |
webhooks:admin | Every /v1/webhooks… endpoint |
tokens:admin | POST /admin/v1/tokens/revoke |
audit:read | GET /v1/audit_logs |
No scope needed: GET /v1/whoami, POST /v1/users, GET /v1/calls,
GET /v1/calls/:id.
New keys created in the dashboard start with users:manage selected.
If you clear it, that key cannot mint user tokens. Use
GET /v1/whoami to see the scopes on a key.
Revoking and rotating
Revoke a key in the dashboard. The next request with it gets
401 revoked_api_key. User JWTs are not tied to the key that minted
them: they keep working until they expire.
To rotate, create the new key, deploy it, then revoke the old one. See Token rotation.
End-user JWTs
One per signed-in end-user. Your backend mints it with
POST /v1/users/:user_id/tokens
(API key with users:manage) and hands it to your app.
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…
- It lasts 1 hour. The mint response gives
expires_in(seconds) andexpires_at. There is no refresh endpoint: mint a new one. - It acts as exactly one user in exactly one tenant. Its
subclaim is the user's poolse id andtntis the tenant id. - The same token opens the realtime socket. See Socket & channels.
| Failure | Response |
|---|---|
| Bad signature, expired, or the user no longer exists | 401 invalid_user_token |
Revoked with POST /admin/v1/tokens/revoke | 401 token_revoked |
The user has is_blocked: true | 401 user_blocked |
| The user is new this month and the plan's monthly active users are full | 402 mau_cap_exceeded (details) |
Typical flow
1. Your app → your backend (your own session)
2. Your backend → POST /v1/users (API key; ensure the user exists)
3. Your backend → POST /v1/users/:user_id/tokens (API key, users:manage)
4. Your backend → your app (the JWT)
5. Your app → poolse REST + socket (the JWT)
Step 2 returns the user's poolse id, which step 3 needs. Store it next
to your own user record so you can skip step 2 later.
Revoking a user's access
- One token:
POST /admin/v1/tokens/revokewith the token. Needstokens:admin. See Users. - Every token, for good: set
is_blocked: truewithPOST /v1/users. Every request with that user's tokens then gets401 user_blocked. Set it back tofalseto restore access.
Both also close the user's open realtime sockets at once. After a revoke, a client holding another valid token reconnects. A blocked user cannot reconnect.
Call grants and control tokens
Calls use two short-lived tokens of their own, so a phone that is locked or closed can act on a call without a user session:
| Token | Where you get it | Lifetime | Accepted by |
|---|---|---|---|
| Ring grant | The grant field of a ring push | 180 seconds | POST /v1/calls/:id/answer, POST /v1/calls/:id/decline |
| Call-control token | control_token in every call session response | 24 hours (the call ending ends it sooner) | POST /v1/calls/:id/hangup, reconnect, ring |
Each works only for the call it was issued for. A user JWT is not
accepted where a grant is required, and the reverse, except that decline
also takes a user JWT. Failures return 401 unauthorized.
On the grant endpoints (answer, decline, hang up, reconnect, ring),
every credential is checked against its user on each request. If the
user is blocked, ring grants, control tokens and the user JWT accepted
by decline are all refused with 401 unauthorized and the message
User is blocked., including grants issued before the block. If the
user no longer exists, they are refused with 401 unauthorized and
Invalid or expired token. See Calls.
Reply grants
A message notification push carries a reply_grant. It lets the phone
reply to that one conversation or mark it read, for 3 days, without a
session. See Notification actions.