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 Tuples
POST /api/v1/fga/tuples
Create authorization relationships (tuples).
Headers
Authorization: Bearer <apiKey>X-Bulwark-Tenant: <tenant-id>X-Bulwark-App-Id: <app-id>— Optional. Filters results to a specific application.
Body
{
"writes": [
{
"user": "user:usr_01j",
"relation": "owner",
"object": "document:doc_abc"
},
{
"user": "agent:agent_01j",
"relation": "viewer",
"object": "document:doc_abc"
}
]
}
Response 200
{
"data": {
"written": 2
}
}
Delete Tuples
DELETE /api/v1/fga/tuples
Body
{
"deletes": [
{
"user": "agent:agent_01j",
"relation": "viewer",
"object": "document:doc_abc"
}
]
}
Response 200
{
"data": {
"deleted": 1
}
}
Check Access
POST /api/v1/fga/check
Check whether a user or agent has a specific relation to an object.
Body
{
"user": "agent:agent_01j",
"relation": "viewer",
"object": "document:doc_abc"
}
Response 200
{
"data": {
"allowed": true
}
}
Batch Check
POST /api/v1/fga/batch-check
Check multiple relationships at once.
Body
{
"checks": [
{ "user": "agent:agent_01j", "relation": "viewer", "object": "document:doc_abc" },
{ "user": "agent:agent_01j", "relation": "editor", "object": "document:doc_abc" }
]
}
Response 200
{
"data": {
"results": [
{ "allowed": true },
{ "allowed": false }
]
}
}
Filter Objects
POST /api/v1/fga/filter
Return only the objects from a list that a user has access to.
Body
{
"user": "agent:agent_01j",
"relation": "viewer",
"type": "document",
"objects": ["document:doc_abc", "document:doc_xyz", "document:doc_123"]
}
Response 200
{
"data": {
"allowed": ["document:doc_abc", "document:doc_123"]
}
}
List Objects
POST /api/v1/fga/list-objects
Return all objects of a type that a user has a specific relation to.
Body
{
"user": "agent:agent_01j",
"relation": "viewer",
"type": "document"
}
Response 200
{
"data": {
"objects": ["document:doc_abc", "document:doc_123"]
}
}