Skip to content
BurnerByte

concepts

API Keys

Scoped API keys for programmatic access.

Creating a Key

bash
POST /api/v1/orgs/{orgId}/teams/{teamId}/api-keys{  "name": "CI Pipeline",  "scopes": ["team.inboxes.create", "team.inboxes.view", "team.emails.view"],  "expires_in": "2160h",  "allowed_ips": ["203.0.113.0/24"]}

The response includes the raw_key field — shown only once (on creation and rotation).

Key Format

API keys are bb_ followed by 64 hex characters (32 bytes of CSPRNG output). The raw key is never stored: the database keeps a SHA-256 hash of the full key plus an 11-character prefix (bb_ and the first 8 hex chars) for display.

Available Scopes

Scopes use the team.* permission key format. When creating or updating a key, specify any combination of these:

ScopePermission
team.viewView team info
team.settings.manageEdit team settings
team.members.viewView team members
team.members.manageAdd/remove team members
team.members.roleChange team member roles
team.domains.viewView domain assignments
team.domains.manageUpdate domain assignment settings
team.webhooks.viewView webhooks and delivery logs
team.webhooks.manageCreate/edit/delete webhooks
team.apikeys.viewView API keys
team.apikeys.manageCreate/revoke/rotate API keys
team.inboxes.viewView team inboxes
team.inboxes.createCreate inboxes
team.inboxes.manageManage inboxes
team.emails.viewView emails
team.analytics.viewView team analytics

The available scopes are derived from the team-scoped permissions in the database. If custom team roles add new permissions, those keys become available as API key scopes automatically.

Authentication

bash
curl -H "Authorization: Bearer bb_your-key-here" \  https://your-instance.com/api/v1/inboxes

The auth middleware detects the bb_ prefix and validates against the API key table instead of JWT. A request is rejected when:

ConditionStatus
The hash matches no key401 invalid API key
expires_at is in the past401 API key expired
The key has been revoked401 API key revoked
The key is deactivated (is_active = false)401 API key disabled
The caller's IP falls outside allowed_ips403 IP not allowed for this API key
The endpoint's required scope is not in the key's scopes403 insufficient scope

A valid key acts as the user who created it — the same org and team memberships apply — but never carries system-admin rights, even when its creator is a system admin. Every accepted request updates request_count, last_used_at and last_used_ip. Each request checks that the key's scopes include the permission required by the endpoint.

Key Management

Get a Single Key

text
GET /api/v1/orgs/{orgId}/teams/{teamId}/api-keys/{keyId}

Update a Key

bash
PATCH /api/v1/orgs/{orgId}/teams/{teamId}/api-keys/{keyId}{  "name": "Updated Name",  "scopes": ["team.inboxes.view", "team.emails.view"],  "is_active": false,  "allowed_ips": ["10.0.0.0/8"]}

Setting is_active to false performs a soft-revoke — the key still exists but is rejected on use. DELETE is also a soft revoke: it stamps revoked_at/revoked_by and the row survives for audit. Rows are hard-deleted only once expired, by the cleanup worker.

Key Rotation

text
POST /api/v1/orgs/{orgId}/teams/{teamId}/api-keys/{keyId}/rotate

Generates a new secret for an existing key. The old secret is immediately invalidated. The response includes the new raw_key.

Revoke a Key

text
DELETE /api/v1/orgs/{orgId}/teams/{teamId}/api-keys/{keyId}

Bulk Revoke

bash
POST /api/v1/orgs/{orgId}/teams/{teamId}/api-keys/bulk-revoke{  "key_ids": ["uuid-1", "uuid-2"]}

Returns the count of revoked keys and any skipped IDs.

IP Allowlist

Keys can be restricted to specific IP addresses or CIDR ranges via the allowed_ips field. When set, requests from IPs outside the allowlist are rejected. An empty list allows all IPs.

Usage Tracking

Each API key tracks:

  • request_count — total number of requests made with the key
  • last_used_at — timestamp of the most recent request
  • last_used_ip — IP address of the most recent request

These fields are returned when listing or fetching keys.