Authentication

poolse has two credentials. They are not interchangeable.

CredentialLooks likeLifetimeActs asLives on
API keypk_live_…Until you revoke itYour backend, for the whole workspace, limited by the key's scopesYour server only
User token (JWT)eyJ…1 hourOne end-user inside your workspaceYour app, held by the SDK

Both go in the same header:

Authorization: Bearer <credential>

What each credential can call

API key (your backend):

  • POST /v1/users: create or update an end-user.
  • POST /v1/users/:user_id/tokens: mint a user token. Needs users:manage.
  • GET /v1/whoami: check the key and see its workspace and scopes.
  • /v1/webhooks*: manage webhooks. Needs webhooks:admin.
  • GET /v1/calls, GET /v1/calls/:id: call history for the workspace.
  • GET /v1/audit_logs: the audit log. Needs audit:read.
  • POST /admin/v1/tokens/revoke: revoke a user token. Needs tokens:admin.

User token (your app, through the SDK):

  • /v1/me, /v1/conversations/*, /v1/messages/*, /v1/attachments/*, /v1/devices, placing and joining calls, and the WebSocket.

Answering, declining and hanging up a call, and the reply / mark-as-read actions on a notification, can also be authorised by short-lived tokens that poolse puts in the call push or the notification, so a closed app can act without a session. See Calls and Notification actions.

A user token can only reach conversations its user is a member of. Endpoint-by-endpoint details are in the API reference.

Scopes

Scopes are set when you create the key and can't be changed later; create a new key instead. A key with no scopes can still upsert users, call whoami and read call history, but it cannot mint user tokens.

ScopeUnlocks
users:managePOST /v1/users/:user_id/tokens (mint user tokens). Every integration needs this.
tokens:adminPOST /admin/v1/tokens/revoke
webhooks:adminEvery /v1/webhooks* endpoint
audit:readGET /v1/audit_logs

A call without the required scope gets 403 forbidden with the message Missing required scope: <scope>.

You create keys in the dashboard (API keys); there is no API for it. New keys start with users:manage ticked; untick it only for a key that never mints tokens. Give each deployment only the scopes it needs.

The mint flow

App          → your /api/chat-token   (your own session cookie or header)
your backend → POST /v1/users/:user_id/tokens   (API key)
poolse       → your backend            { token, token_type, expires_in, expires_at }
your backend → App                     { token }
App          → poolse                  (token, on every REST call and the WebSocket)

The :user_id is poolse's id for the user (a UUID), which POST /v1/users returns. It is not your external_id. An id that isn't a UUID, or a user that doesn't exist, gets 404.

The mint response:

{
  "token": "eyJhbGciOi…",
  "token_type": "bearer",
  "expires_in": 3600,
  "expires_at": "2026-10-27T13:00:00Z"
}

Your token route must:

  • Authenticate the caller with your own session. Only mint a token for the user who is actually signed in.
  • Return a fresh token each time. The SDK caches the token in memory, asks your getToken for a new one 30 seconds before it expires, and asks again once if a request comes back 401.

Minting is limited to 60 calls per minute per API key; see Limits & quotas.

Revoking a user token

Call POST /admin/v1/tokens/revoke with { "token": "<the JWT>" } and a key that has tokens:admin. From then on, until the token would have expired, REST calls with it get 401 token_revoked and new WebSocket connections with it are refused. Revoking also closes every WebSocket the user has open at that moment; connections using another, still-valid token reconnect on their own. Use it when a user signs out or you suspect a token leaked. Details: Users.

Blocking a user (is_blocked: true on POST /v1/users) also stops their tokens from working on REST and the WebSocket, and closes the sockets they have open.

Rotating an API key

  1. Create a new key with the same scopes.
  2. Deploy it to your backend.
  3. Revoke the old key in the dashboard.

A revoked key is refused on its next request. User tokens minted with the old key keep working until they expire (at most one hour), because a token is not tied to the key that minted it. See Token rotation.

Authentication concepts · poolse docs