API Reference

Bulwark exposes a REST API over HTTPS. All request and response bodies are JSON.

Base URL

https://api.bulwarkauth.com/api/v1

For self-hosted instances:

https://your-domain.com/api/v1

Authentication

API Key (server-to-server)

Authorization: Bearer bwk_live_<key>

User JWT (client requests)

Authorization: Bearer eyJ...

Agent Biscuit Token

Authorization: Bearer En0KH...

Required and Optional Headers

Tenant Header

All requests must include the tenant header to identify your workspace:

X-Bulwark-Tenant: <tenant-id>

Application Header

To scope results to a specific application, include the optional application header:

X-Bulwark-App-Id: <app-id>

When present, list endpoints such as sessions and audit events return only records associated with the specified application. If omitted, results include records from all applications in the workspace.

Example with both headers

curl https://api.bulwarkauth.com/api/v1/audit \
  -H "Authorization: Bearer bwk_live_..." \
  -H "X-Bulwark-Tenant: tenant_01j..." \
  -H "X-Bulwark-App-Id: app_01j..."

Response Format

Responses are not wrapped in an envelope. A successful response is the resource itself, encoded directly as JSON. All field names use snake_case.

Success — single resource

{
  "id": "usr_01j...",
  "email": "[email protected]",
  "created_at": "2026-06-01T00:00:00Z"
}

Success — list

List endpoints return the items under a named key alongside a count (and, where paginated, offset / limit):

{
  "agents": [ ... ],
  "count": 2
}

Error

Errors return a flat object with a single human-readable error message; the HTTP status code conveys the category. There are no machine-readable error codes.

{
  "error": "email or password is incorrect"
}

Common HTTP Status Codes

| HTTP | Meaning | |------|---------| | 400 | Malformed request (bad body, missing required field/header) | | 401 | Missing/invalid/expired credentials | | 403 | Authenticated but not permitted (insufficient scope or role) | | 404 | Resource not found | | 409 | Conflict (e.g. already exists / already in that state) | | 410 | Gone (e.g. an expired challenge or token) | | 422 | Unprocessable (e.g. invalid verification token) | | 429 | Rate limited — see below |

Rate Limits

When a rate limit is exceeded the API responds with 429 Too Many Requests and a Retry-After header (in seconds):

Retry-After: 60

Rate limiting is applied to sensitive endpoints (e.g. login, passkey begin/finish) and is configurable per deployment.

Pagination

List endpoints use offset/limit pagination:

GET /api/v1/ciba/requests?limit=20&offset=0

The response includes the items, a count, and the echoed offset:

{
  "requests": [ ... ],
  "total": 42,
  "offset": 0
}