Get started

Authentication and keys

API keys, console session, scopes, environments, and security best practices.

Updated November 12, 2026View as Markdown

Two kinds of credentials#

Console sessionAPI key
Formatses_…pk_test_… (sandbox) or pk_live_… (production)
Used forManaging your account: apps, keys, billing, logsCalling the API from your server: connections, messages, templates, webhooks, usage
Obtained viaPOST /auth/signup or POST /auth/loginPOST /apps/{appId}/keys
Lifetime30 days, revocable (POST /auth/logout)Until revoked
HeaderAuthorization: Bearer ses_…Authorization: Bearer pk_…

The routes in the "Authentication" and "Apps" sections, GET /billing/overview, and GET /logs/requests use the session. All the others use an API key. Each page of the reference states its authentication mode.

The console uses the same routes#

The API-key routes (connections, messages, templates, webhooks, usage) also accept the console session, with an X-Environment-Id header that specifies the target environment (an environment's identifier is returned by GET /apps). The signed-in member then has all the scopes of their account. This is what the Bref Point Inbox console does. These calls do not appear in the request log, which is reserved for API keys.

For use from your server, always use a key: its rights are limited by scope and its environment is fixed.

Create a key#

bash
curl -X POST "$INBOX_API/apps/APP_ID/keys" \
  -H "Authorization: Bearer $INBOX_SESSION" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Local dev",
    "environment": "sandbox",
    "scopes": [
      "messages:send",
      "messages:read",
      "connections:manage",
      "connections:read",
      "templates:manage",
      "webhooks:manage",
      "usage:read"
    ]
  }'
Response · 201 Created
{
  "id": "5ef37bc9-8aac-43ef-81b1-6bb3bdb66e78",
  "name": "Local dev",
  "prefix": "pk_test_bwcA3X-A",
  "environment": "sandbox",
  "scopes": [
    "messages:send",
    "messages:read",
    "connections:manage",
    "connections:read",
    "templates:manage",
    "webhooks:manage",
    "usage:read"
  ],
  "lastUsedAt": null,
  "revokedAt": null,
  "createdAt": "2026-11-12T08:33:45.382Z",
  "secret": "pk_test_26f602c48c47ef280792fd3a506473688df70b80118"
}

Scopes#

A key can only do what you grant it. A call that requires a scope the key doesn't have is rejected with 403 forbidden, and the message names the missing scope.

ScopeAllows
messages:sendSending messages (POST /messages) and using the sandbox simulator
messages:readReading a message or the history (GET /messages)
connections:manageCreating connected businesses and connecting WhatsApp accounts
connections:readListing connected businesses, their accounts, and their numbers
templates:manageCreating, listing, syncing, and deleting templates
webhooks:manageManaging webhook endpoints and viewing/replaying deliveries
usage:readReading usage (GET /usage)

Environments#

Each app has a sandbox environment and a production environment. A key belongs to one environment only and sees only its data: connected businesses, numbers, messages, webhook endpoints.

  • A pk_test_… key can never touch production, and vice versa.
  • The sandbox simulator rejects production keys (403 sandbox_only).
  • The sandbox is neither rate limited nor billed.

Use a key#

bash
curl "$INBOX_API/messages" -H "Authorization: Bearer $INBOX_KEY"

A missing, invalid, or revoked key returns 401 unauthorized.

Real error · HTTP 401
{
  "error": {
    "code": "unauthorized",
    "message": "API key required (Authorization: Bearer pk_…)",
    "requestId": "req_1c1b9300-f2d4-4387-88b8-7d66222d7497"
  }
}

List and revoke#

bash
curl "$INBOX_API/apps/APP_ID/keys" \
  -H "Authorization: Bearer $INBOX_SESSION"
Response · 200 OK
[
  {
    "id": "5ef37bc9-8aac-43ef-81b1-6bb3bdb66e78",
    "name": "Local dev",
    "prefix": "pk_test_bwcA3X-A",
    "environment": "sandbox",
    "scopes": [
      "messages:send",
      "messages:read",
      "connections:manage",
      "connections:read",
      "templates:manage",
      "webhooks:manage",
      "usage:read"
    ],
    "lastUsedAt": null,
    "revokedAt": null,
    "createdAt": "2026-11-12T08:33:45.382Z"
  }
]

lastUsedAt shows the last use: a key that hasn't been used for weeks is a good candidate for revocation.

bash
curl -X DELETE "$INBOX_API/apps/APP_ID/keys/KEY_ID" \
  -H "Authorization: Bearer $INBOX_SESSION"
Response · 204 No Content
(no content)

Revocation is immediate and permanent.

Best practices#

  • Call the API from your server, never from a browser or a mobile app. A key placed in client code is public. The API also doesn't allow browser calls (no CORS).
  • Store keys in environment variables or a secrets manager, never in source code or a repository.
  • Use one key per service and per environment: you can revoke one without interrupting the others.
  • Rotate your keys: create the new one, deploy it, check that the old key's lastUsedAt no longer changes, then revoke it.
  • Monitor the request log: every call appears there with its status and its requestId.