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

Success

{
  "data": { ... },
  "meta": {
    "requestId": "req_01j..."
  }
}

Error

{
  "error": {
    "code": "INVALID_CREDENTIALS",
    "message": "Email or password is incorrect",
    "requestId": "req_01j..."
  }
}

Common Error Codes

| Code | HTTP | Description | |------|------|-------------| | INVALID_CREDENTIALS | 401 | Wrong email/password | | TOKEN_EXPIRED | 401 | JWT or Biscuit token expired | | INSUFFICIENT_SCOPE | 403 | Token lacks required scope | | NOT_FOUND | 404 | Resource not found | | RATE_LIMITED | 429 | Too many requests | | TENANT_REQUIRED | 400 | Missing X-Bulwark-Tenant header |

Rate Limits

| Plan | Requests/min | |------|--------------| | Free | 60 | | Pro | 600 | | Enterprise | Custom |

Rate limit headers returned on every response:

X-RateLimit-Limit: 600
X-RateLimit-Remaining: 598
X-RateLimit-Reset: 1709558400

Pagination

List endpoints support cursor-based pagination:

GET /api/v1/agents?limit=20&cursor=<cursor>

Response includes:

{
  "data": [...],
  "pagination": {
    "cursor": "next_cursor_value",
    "hasMore": true
  }
}