Admin Endpoints
Platform administration — stats, user management, and session oversight.
Authentication
/api/v1/admin/stats and /api/v1/admin/sessions endpoints require a Bulwark-issued JWT with the admin role.
/api/v1/admin/users and /api/v1/admin/users/{id} endpoints accept either:
- a JWT with the
adminrole (dashboard / MCP use), OR - an API key carrying the matching scope —
users:readfor list/get,users:writefor create/update/delete.
/api/v1/admin/users/{id}/sessions endpoints (list, revoke one, revoke all) follow the same dual-auth model with the sessions:read / sessions:write scopes. Use these to power "Active sessions" UIs in your tenant portal.
API-key auth is the path backend services should use (e.g. auto-creating users from an invitation-accept flow on a consumer app, or rendering an account-security page that lists devices). Send the key as Authorization: Bearer bwk_live_... plus X-Bulwark-Tenant: <workspace-id>.
Keys are created + scoped via POST /api/v1/api-keys or the admin dashboard's picker.
Platform Stats
GET /api/v1/admin/stats
Headers
Authorization: Bearer <adminToken>X-Bulwark-Tenant: <tenant-id>X-Bulwark-App-Id: <app-id>— Optional. Filters results to a specific application.
Response 200
{
"total_users": 1240,
"active_users": 892,
"suspended_users": 5,
"total_agents": 78,
"active_agents": 64,
"active_sessions": 203,
"events_24h": 48920
}
List Users
GET /api/v1/admin/users
Query Parameters
limit— Results per page (default: 25)offset— Pagination offset (default: 0)search— Search by email or namestatus— Filter by status (active,suspended)mfa— Filter by TOTP enrollment (enrolled,not_enrolled)
Response 200
{
"users": [
{
"id": "usr_01j...",
"tenant_id": "019d...",
"email": "[email protected]",
"email_verified": true,
"display_name": "Jane Doe",
"status": "active",
"roles": ["user"],
"created_at": "2026-01-15T00:00:00Z",
"updated_at": "2026-01-15T00:00:00Z",
"last_login_at": "2026-03-17T22:00:00Z",
"credential_count": 1,
"session_count": 2,
"mfa_enrolled": false
}
],
"count": 1,
"offset": 0,
"limit": 25
}
Get User
GET /api/v1/admin/users/{id}
Response 200
{
"id": "usr_01j...",
"tenant_id": "019d...",
"email": "[email protected]",
"email_verified": true,
"display_name": "Jane Doe",
"status": "active",
"roles": ["user"],
"created_at": "2026-01-15T00:00:00Z",
"updated_at": "2026-01-15T00:00:00Z",
"last_login_at": "2026-03-17T22:00:00Z",
"credential_count": 1,
"session_count": 2,
"mfa_enrolled": false
}
Create User
POST /api/v1/admin/users
Creates a user in the workspace's user pool. Intended for programmatic provisioning from a consumer app — e.g. auto-creating a Bulwark user the moment an invitation is accepted, so the invitee doesn't have to complete a separate signup.
Auth
Requires users:write scope (for API key) or admin role (for JWT).
Body
{
"email": "[email protected]",
"password": "optional-initial-password",
"display_name": "New User",
"email_verified": true,
"roles": ["user"]
}
email(required) — must be unique within the workspace user pool.password(optional) — if omitted, the user has no login credential and must complete a password-reset (or be provisioned a WebAuthn / passkey credential) before they can sign in.display_name,email_verified,roles— optional.rolesdefaults to["user"].
Response 201
Returns the full user detail — the caller can map the returned id to their own user record.
{
"id": "usr_01j...",
"tenant_id": "019d...",
"email": "[email protected]",
"email_verified": true,
"display_name": "New User",
"status": "active",
"roles": ["user"],
"created_at": "2026-04-16T16:00:00Z",
"updated_at": "2026-04-16T16:00:00Z",
"credential_count": 0,
"session_count": 0,
"mfa_enrolled": false
}
Errors
400— missing or invalidemail.409— email already exists in the workspace user pool.401/403— authentication or scope/role check failed.
Update User
PATCH /api/v1/admin/users/{id}
All fields are optional — send only the fields you want to change.
Body
{
"email": "[email protected]",
"status": "suspended",
"roles": ["admin"]
}
email(optional) — new login email; must be unique within the workspace.status(optional) —activeorsuspended.roles(optional) — replaces the user's role list.
Response 200
{ "status": "updated" }
Delete User
DELETE /api/v1/admin/users/{id}
Requires users:write scope (API key) or admin role (JWT).
Response 200
{ "status": "deleted" }
Returns 404 if the user does not exist.
Reset User MFA
DELETE /api/v1/admin/users/{id}/mfa
Removes a user's TOTP devices and any remaining backup codes — e.g. when a user has lost their authenticator. Admin JWT.
Response 200
{
"status": "reset",
"totp_removed": 1,
"backup_codes_removed": 8
}
Returns 501 if the MFA service is not configured for this deployment.
List User Consents
GET /api/v1/admin/users/{id}/consents
Lists the user's active OAuth2 consent grants (which connected apps they've authorized). Admin JWT.
Response 200
{
"consents": [
{
"id": "cns_01j...",
"client_id": "bwk_...",
"client_name": "Acme Data Exporter",
"description": "Exports customer reports",
"scopes": ["read:customers"],
"granted_at": "2026-05-01T00:00:00Z"
}
]
}
description is omitted when the client has none.
Revoke User Consent
DELETE /api/v1/admin/users/{id}/consents/{consent_id}
Revokes one consent grant, cutting off the connected app's access on the user's behalf. Admin JWT.
Response 200
{ "status": "revoked" }
Returns 422 if the consent can't be revoked (e.g. not found).
List User Login Sessions
One row per signed-in device. Each row corresponds to a refresh-token family. Device fields (browser, os, device_type) are parsed server-side from the User-Agent captured at login. Geo fields (location.city, region, country) are populated from MaxMind GeoLite2 when configured; absent otherwise.
GET /api/v1/admin/users/{id}/sessions
Authentication
Either an admin JWT or an API key with the sessions:read scope.
Headers
Authorization: Bearer <adminToken|bwk_live_...>X-Bulwark-Tenant: <tenant-id>
Response 200
{
"sessions": [
{
"id": "0193d07e-d8cb-7819-b0a1-48c90e602461",
"family_id": "0193d07e-aaaa-7819-b0a1-48c90e602461",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
"browser": "Chrome",
"browser_version": "125",
"os": "macOS",
"os_version": "14.5",
"device_type": "desktop",
"location": {
"city": "Salt Lake City",
"region": "UT",
"country": "US"
},
"started_at": "2026-05-01T12:00:00Z",
"last_active_at": "2026-05-09T08:30:00Z",
"expires_at": "2026-05-31T12:00:00Z"
}
],
"count": 1
}
Field notes:
idis what you pass to the revoke endpoints below.locationis omitted entirely (not just empty) when no geo data is available, so the UI can hide the column cleanly.last_active_atadvances on every refresh-token rotation — useful as a "last seen" indicator.- Sessions created by users who logged in via passkey or OAuth/social currently do NOT appear here — those auth flows do not issue refresh tokens. Only password logins and refresh-token rotations populate the sessions table.
Revoke One User Session
Signs the user out of one specific device. The other devices stay signed in.
DELETE /api/v1/admin/users/{id}/sessions/{session_id}
Authentication
Either an admin JWT or an API key with the sessions:write scope.
Response 200
{ "status": "revoked" }
Returns 404 if the session id doesn't exist or doesn't belong to (user_id, tenant_id) — guards against cross-tenant id-guessing.
Revoke ALL User Sessions
Signs the user out of every device. Use for forced sign-out (compromise response, account suspension, etc.).
DELETE /api/v1/admin/users/{id}/sessions
Authentication
Either an admin JWT or an API key with the sessions:write scope.
Response 200
{
"status": "revoked",
"revoked": 4
}
revoked is the count of sessions that were active when the call landed.
List Agent Sessions
Different concept from user login sessions above. This lists active agent execution sessions (AI-agent runs against the credential proxy), not user devices. Most tenant-portal use cases want the user-login endpoint above.
GET /api/v1/admin/sessions
Authentication
Admin JWT only (no API key support on this endpoint).
Response 200
{
"sessions": [
{
"id": "...",
"agent_id": "...",
"agent_name": "billing-sync",
"status": "active",
"ttl_seconds": 3600,
"max_uses": 100,
"current_uses": 23,
"expires_at": "2026-05-09T13:00:00Z",
"created_at": "2026-05-09T12:00:00Z"
}
],
"count": 1,
"offset": 0,
"limit": 25
}
Revoke Agent Session
DELETE /api/v1/admin/sessions/{id}
Immediately ends one agent execution session (from the list above). Admin JWT.
Response 200
{ "status": "revoked" }
Organization Settings
Org-wide configuration — auth methods, session TTLs, security policy, branding, and email templates. These are distinct from the per-application settings under /api/v1/settings/applications.
GET /api/v1/settings
PUT /api/v1/settings
Authentication
Both endpoints require a Bulwark-issued JWT with the admin role.
Response 200
{
"settings": {
"auth_methods": {
"password_enabled": true,
"passkeys_enabled": true,
"mfa_policy": "optional"
},
"session_config": {
"access_token_ttl": 900,
"refresh_token_ttl": 2592000,
"session_ttl": 720
},
"security_config": {
"rate_limit_enabled": true,
"email_verify_required": true,
"registration_enabled": true,
"audit_retention_days": 90
},
"branding": { "...": "see Branding endpoints" },
"email_templates": { "...": "see Email Templates" }
}
}
Audit retention
security_config.audit_retention_days controls how long audit events are kept before the daily cleanup worker deletes them.
- Default:
90days. A value of0means "use the default," not "keep forever." - Clamped to
[7, 365]on write and in the SQL retention function — values outside the range are silently clamped, so retention can never be disabled or grow unbounded via the API.
PUT /api/v1/settings accepts a partial body; only the fields you send are updated.
Waitlist
A simple pre-launch waitlist. Joining is public; listing and removing entries require admin access.
Join the waitlist
POST /api/v1/waitlist
Public — requires only the X-Bulwark-Tenant header.
Body
{
"email": "[email protected]",
"name": "Jane Doe",
"company": "Acme"
}
email is required; name and company are optional. Duplicate emails are ignored.
Response 200
{ "message": "You're on the list! We'll be in touch soon." }
Returns 400 for a missing or malformed email.
List entries
GET /api/v1/admin/waitlist
Admin JWT.
{
"entries": [
{
"id": "wl_01j...",
"email": "[email protected]",
"name": "Jane Doe",
"company": "Acme",
"created_at": "2026-06-01T00:00:00Z"
}
],
"count": 1
}
name and company are omitted when empty.
Remove an entry
DELETE /api/v1/admin/waitlist/{id}
Admin JWT. Returns 200 { "status": "deleted" }, or 404 if the entry does not exist.
Application Isolation
Moves an application from shared-workspace mode to isolated mode, where it no longer shares users, agents, FGA tuples, API keys, or webhooks with the rest of the workspace. Preview first to see what becomes inaccessible, then isolate. Admin JWT.
Preview
GET /api/v1/admin/apps/{id}/isolation-preview
Advisory counts of workspace-scoped resources the app would lose access to after isolation.
Response 200
{
"app_id": "ten_01j...",
"app_name": "Acme Mobile",
"workspace_id": "wsp_01j...",
"current_mode": "shared",
"workspace_users": 1240,
"workspace_fga_tuples": 87,
"workspace_agents": 12,
"workspace_api_keys": 4,
"workspace_webhooks": 2
}
Isolate
POST /api/v1/admin/apps/{id}/isolate
Flips the app to isolated mode.
Response 200
{
"app_id": "ten_01j...",
"new_mode": "isolated",
"audit_id": "evt_01j..."
}
audit_id references the audit event recording the change.