api
API Reference
BurnerByte REST API overview.
Base URL
All API endpoints are prefixed with /api/v1.
Authentication
Most endpoints require authentication via one of:
JWT Bearer Token
Authorization: Bearer <access_token>Obtain tokens via POST /auth/login or POST /auth/register. Access tokens expire after 15 minutes (configurable). Use POST /auth/refresh with the refresh token to get new tokens, and POST /auth/logout to revoke the current session. GET /auth/sso-status returns SSO configuration and password_policy settings.
Browser clients can pass use_cookie: true on login, register, login/resolve, refresh, and sso/exchange: the refresh token is then delivered as an httpOnly cookie (scoped to /api/v1/auth) and omitted from the JSON response, keeping it out of script-readable storage. refresh and logout fall back to that cookie when the body carries no token. API clients that never send the flag keep receiving both tokens in the body.
Session Limit Enforcement
When a user logs in via password and has reached their maximum number of active sessions, the API returns a 409 Conflict instead of automatically revoking the oldest session. The response includes a pending login token and the list of active sessions.
Login Response (409 — Session Limit Reached)
{ "error": "session limit reached", "code": "session_limit", "pending_token": "a1b2c3d4...", "sessions": [ { "id": "...", "ip_address": "192.168.1.1", "user_agent": "Mozilla/5.0 ...", "last_used_at": "2026-04-28T10:30:00Z", "created_at": "2026-04-25T08:00:00Z" } ], "limit": 5}Resolving the Conflict
To complete the login, call POST /auth/login/resolve with the pending token and the ID of the session to revoke:
{ "pending_token": "a1b2c3d4...", "revoke_session_id": "550e8400-e29b-41d4-a716-446655440000"}On success, returns the same 200 response as a normal login with user and tokens. If the session limit is still exceeded (race condition), returns another 409 with a fresh pending token and updated session list.
The pending token expires after 5 minutes. SSO logins go through the same interactive flow: the callback redirects to /login#session_conflict=true&pending_token=<token>&limit=<n>, and the frontend fetches the session list with GET /auth/login/pending-sessions?token=<token> before calling POST /auth/login/resolve. Only if the pending-login store (Redis) is unavailable does either path fall back to auto-revoking the oldest session.
The default session limit is 5 per user, configurable via defaults.max_sessions_per_user in config or PUT /admin/platform. Per-user overrides can be set via PATCH /admin/users/:id with max_sessions.
API Key
Authorization: Bearer bb_<your-api-key>API keys are scoped to a team with specific permissions. See API Keys.
Pagination
List endpoints support pagination:
GET /inboxes?page=1&per_page=20Response:
{ "data": [], "total": 42, "page": 1, "per_page": 20, "total_pages": 3}Errors
{"error": "inbox not found", "code": "not_found"}| Code | Meaning | code value |
|---|---|---|
| 400 | Bad request (validation error) | validation_error |
| 401 | Unauthorized (missing or invalid token) | unauthenticated |
| 403 | Forbidden (insufficient permissions) | forbidden |
| 404 | Resource not found | not_found |
| 409 | Conflict (duplicate resource or session limit reached) | conflict or session_limit |
| 429 | Rate limit exceeded | rate_limited |
| 500 | Internal server error | internal_error |
Rate Limiting
Headers included in every response:
X-RateLimit-Limit: 100X-RateLimit-Remaining: 99X-RateLimit-Reset: 1709251200Endpoint Groups
| Group | Endpoints |
|---|---|
| Setup | GET /setup/status (adds a credential-stripped datastores object while setup is incomplete), POST /setup/complete, POST /setup/test-smtp, POST /setup/test-storage |
| Demo (Try It) | GET /try/status, POST /try/inbox, GET /try/inbox/:inboxId/emails — public, see Demo Mode |
| Auth | POST /auth/register, POST /auth/login, POST /auth/login/resolve, POST /auth/refresh, POST /auth/logout, GET /auth/me, PATCH /auth/me, PUT /auth/me/password, DELETE /auth/me, POST /auth/forgot-password, POST /auth/reset-password, GET /auth/verify-email/:token, GET /auth/sso-status, GET /auth/datetime-settings, GET /auth/login/pending-sessions, POST /auth/sso/exchange |
| Sessions | GET /auth/sessions, DELETE /auth/sessions/:id, DELETE /auth/sessions |
| SSO | GET /auth/sso/:provider, GET /auth/sso/:provider/callback |
| SSO Identities | GET /auth/me/sso, DELETE /auth/me/sso/:provider |
| Orgs | POST /orgs, GET /orgs, GET /orgs/:id, PATCH /orgs/:id, DELETE /orgs/:id |
| Org Settings | GET /orgs/:id/settings, PATCH /orgs/:id/settings, PUT /orgs/:id/settings |
| Members | POST /orgs/:id/members, POST /orgs/:id/members/add, GET /orgs/:id/members, GET /orgs/:id/members/me, GET /orgs/:id/members/search, PATCH /orgs/:id/members/:uid, POST /orgs/:id/members/:uid/deactivate |
| Invites | POST /orgs/:id/invites, POST /orgs/:id/invites/bulk, GET /orgs/:id/invites, DELETE /orgs/:id/invites/:iid, GET /invites/:token/preview, POST /invites/:token/accept |
| Domains | POST /orgs/:id/domains, GET /orgs/:id/domains, GET /orgs/:id/domains/:did, PATCH /orgs/:id/domains/:did, DELETE /orgs/:id/domains/:did, POST /orgs/:id/domains/:did/verify, GET /orgs/:id/domains/:did/impact, GET /orgs/:id/domains/:did/verification-history, POST /orgs/:id/domains/:did/transfer, POST /orgs/:id/domains/bulk-verify, POST /orgs/:id/domains/bulk-delete |
| Teams | POST /orgs/:id/teams, GET /orgs/:id/teams, GET /orgs/:id/teams/:tid, PATCH /orgs/:id/teams/:tid, DELETE /orgs/:id/teams/:tid, POST .../teams/:tid/archive, POST .../teams/:tid/restore, GET .../teams/:tid/impact, POST .../teams/:tid/leave, POST .../teams/:tid/transfer |
| Team Members | POST .../teams/:tid/members, GET .../teams/:tid/members, POST .../teams/:tid/members/bulk-add, POST .../teams/:tid/members/bulk-remove, PATCH .../teams/:tid/members/:uid, DELETE .../teams/:tid/members/:uid |
| Domain Assignments | POST .../teams/:tid/domains, GET .../teams/:tid/domains, PATCH .../teams/:tid/domains/:did, DELETE .../teams/:tid/domains/:did, GET /my/domains |
| Inboxes | POST /inboxes, GET /inboxes, GET /inboxes/:id, POST /inboxes/:id/extend, DELETE /inboxes/:id, GET /orgs/:id/teams/:tid/inboxes |
| Emails | GET /inboxes/:id/emails, GET /emails/:id, PATCH /emails/:id, DELETE /emails/:id, GET /emails/:id/attachments/:aid, POST /inboxes/:id/emails/mark-all-read |
| Webhooks | POST .../webhooks, GET .../webhooks, PATCH .../webhooks/:wid, DELETE .../webhooks/:wid, GET .../webhooks/:wid/deliveries, GET .../webhooks/:wid/stats |
| API Keys | POST .../api-keys, GET .../api-keys, GET .../api-keys/:kid, PATCH .../api-keys/:kid, DELETE .../api-keys/:kid, POST .../api-keys/:kid/rotate, POST .../api-keys/bulk-revoke |
| Analytics | GET /orgs/:id/analytics, GET /orgs/:id/analytics/emails-per-day, GET /orgs/:id/analytics/insights, GET /orgs/:id/analytics/domain-series, GET .../teams/:tid/analytics, GET .../teams/:tid/analytics/emails-per-day, GET .../teams/:tid/analytics/insights |
| Audit | GET /orgs/:id/audit, GET /orgs/:id/audit/export, GET /admin/audit (system admin) |
| Files | GET /files?key= — local-filesystem attachment passthrough |
| Admin | GET /admin/stats, GET /admin/orgs, GET /admin/health, GET /admin/users, PATCH /admin/users/:id, DELETE /admin/users/:id, DELETE /admin/users/:id/sessions, POST /admin/users/:id/migrate-auth, GET /admin/platform, PUT /admin/platform, GET /admin/version, POST /admin/infra/test-smtp, POST /admin/infra/test-storage, GET/PUT /admin/config/mailer, GET/PUT /admin/config/storage |
| Admin SSO | GET /admin/sso/providers, POST /admin/sso/providers, GET /admin/sso/providers/:pid, PUT /admin/sso/providers/:pid, DELETE /admin/sso/providers/:pid, POST /admin/sso/test, POST /admin/sso/providers/:pid/domain-mappings, GET /admin/sso/providers/:pid/domain-mappings, PUT /admin/sso/providers/:pid/domain-mappings/:mid, DELETE /admin/sso/providers/:pid/domain-mappings/:mid, POST /admin/sso/domain-mappings/preview |
| Admin Roles | POST /admin/roles, PATCH /admin/roles/:roleId, DELETE /admin/roles/:roleId |
| Notifications | GET /notifications, POST /notifications/mark-all-read, PATCH /notifications/:id/read, DELETE /notifications, DELETE /notifications/:id |
| Roles | GET /roles |
| WebSocket | POST /ws/ticket, GET /ws/inboxes/:id, GET /ws/notifications, GET /ws/admin-stats |
| Docs | GET /docs, GET /api/v1/docs/openapi.json |
| Health | GET /healthz, GET /readyz, GET /metrics — root level, not under /api/v1; the metrics path is set by metrics.path, served only when metrics.enabled, and restricted to loopback and private source IPs |
The domain and team POST .../transfer endpoints require a system admin.
WebSocket Endpoints
WebSocket endpoints authenticate with a single-use ticket, not the JWT directly. Call POST /api/v1/ws/ticket with your Bearer token to obtain a ticket (a UUID stored in Redis with a 30-second TTL), then connect with ?ticket=<ticket>.
Inbox Stream
GET /ws/inboxes/{inboxId}?ticket=<ticket>Streams new emails to a specific inbox in real-time. Messages are JSON with type: "email.received" and the email data in data.
Notification Stream
GET /ws/notifications?ticket=<ticket>Streams all notifications for the authenticated user. Powers the notification bell in the UI. Messages include type (e.g., email.received, inbox.created, inbox.expired) and data with event details.
Admin Stats Stream
GET /ws/admin-stats?ticket=<ticket>Requires system admin. Streams real-time platform statistics. Messages have type: "admin.stats" with data containing:
| Field | Type | Description |
|---|---|---|
total_emails | number | Total emails received |
active_inboxes | number | Currently active inboxes |
total_domains | number | Registered domains |
total_users | number | Platform users |
total_teams | number | Teams |
storage_used_bytes | number | Total storage used |
Used by the dashboard for live stat updates when auto-refresh is enabled.
OpenAPI Specification
A machine-readable OpenAPI 3.0 specification is available at:
GET /docs/openapi.jsonThis can be imported into tools like Postman, Insomnia, or used to generate client SDKs.