FGA Endpoints
Fine-Grained Authorization — relationship-based access control for agents, users, and resources.
Authentication
FGA endpoints accept either a Bulwark-issued user JWT or an API key (bwk_live_* / bwk_test_*). Both are sent as Authorization: Bearer <token>. The server dispatches by token prefix: tokens beginning with bwk_ are validated against the API-key store; everything else is verified as a JWT.
X-Bulwark-Tenant is required on every FGA request and drives row-level isolation. An API key may only be used against the tenant it was issued for; a mismatch returns 403.
Required scopes
| Endpoint | JWT requirement | API-key scope |
| --------------------------------------------------------------------------------------- | ---------------------------------------- | ------------- |
| POST /api/v1/fga/check, POST /api/v1/fga/filter, POST /api/v1/fga/list-objects, GET /api/v1/fga/tuples | Any valid JWT for the tenant | fga:read |
| POST /api/v1/fga/tuples, DELETE /api/v1/fga/tuples | JWT with admin role | fga:write |
JWT principals do not declare scopes — the role claim is the authorization signal. API-key principals carry an explicit scope set declared at key creation; missing the required scope returns 403 with the scope name in the body so callers can correct the key configuration.
API keys are created and scoped via the dashboard or POST /api/v1/api-keys (see API Key Endpoints). The set of valid scope strings is enumerable at GET /api/v1/api-keys/scopes.
Write Tuple
POST /api/v1/fga/tuples
Create a single authorization relationship (tuple). To create multiple tuples, call this endpoint once per tuple.
Headers
Authorization: Bearer <apiKey>X-Bulwark-Tenant: <tenant-id>
Body
{
"user_type": "user",
"user_id": "usr_01j",
"relation": "owner",
"object_type": "document",
"object_id": "doc_abc"
}
All five fields are required. Valid relations: owner, editor, viewer, can_read, can_write, member, parent.
Response 201
Returns the written tuple:
{
"id": "tpl_01j...",
"tenant_id": "019d...",
"user_type": "user",
"user_id": "usr_01j",
"relation": "owner",
"object_type": "document",
"object_id": "doc_abc",
"created_at": "2026-03-20T10:00:00Z"
}
Delete Tuple
DELETE /api/v1/fga/tuples
Delete a single authorization relationship (tuple). To delete multiple tuples, call this endpoint once per tuple.
Body
{
"user_type": "user",
"user_id": "usr_01j",
"relation": "owner",
"object_type": "document",
"object_id": "doc_abc"
}
All five fields are required and must exactly match the tuple to be deleted.
Response 200
{ "status": "deleted" }
List Tuples
GET /api/v1/fga/tuples
Returns all tuples stored for the tenant, optionally filtered by object.
Query Parameters
object_type— Optional. Filter to tuples for this object type (e.g.document).object_id— Optional. Filter to tuples for this specific object ID.offset— Pagination offset (default: 0).limit— Results per page (default: 50).
Response 200
{
"tuples": [
{
"id": "tpl_01j...",
"tenant_id": "019d...",
"user_type": "user",
"user_id": "usr_01j",
"relation": "owner",
"object_type": "document",
"object_id": "doc_abc",
"created_at": "2026-03-20T10:00:00Z"
}
],
"total": 1,
"offset": 0,
"limit": 50
}
Check Access
POST /api/v1/fga/check
Check whether a user or agent has a specific relation to an object.
Body
{
"user_type": "user",
"user_id": "usr_01j",
"relation": "viewer",
"object_type": "document",
"object_id": "doc_abc"
}
All five fields are required.
Response 200
{
"allowed": true,
"reason": "direct"
}
reason is omitted when empty. allowed: false means the principal does not have the requested relation.
Filter Objects
POST /api/v1/fga/filter
Given a list of object IDs, return only those the user has the requested relation on.
Body
{
"user_type": "user",
"user_id": "usr_01j",
"relation": "viewer",
"object_type": "document",
"object_ids": ["doc_abc", "doc_xyz", "doc_123"]
}
user_type, user_id, relation, and object_type are required. object_ids is the list of bare IDs to test.
Response 200
{
"allowed_ids": ["doc_abc", "doc_123"],
"total": 3,
"allowed": 2,
"denied": 1
}
List Objects
POST /api/v1/fga/list-objects
Return all object IDs of a given type that the user has a specific relation to.
Body
{
"user_type": "user",
"user_id": "usr_01j",
"relation": "viewer",
"object_type": "document",
"limit": 50,
"offset": 0
}
user_type, user_id, relation, and object_type are required. limit and offset are optional (default: no limit / 0).
Response 200
{
"object_ids": ["doc_abc", "doc_123"],
"total": 2
}