Admin API reference
All Admin API endpoints are under /admin/api/ and require:
X-Admin-API-Key: <key>
The key is configured via the PBAC_ADMIN_API_KEY environment variable (default in dev: dev-admin-key).
All request and response bodies are application/json. List endpoints accept an optional tenantId query parameter to filter by tenant.
Standard HTTP status codes apply: 200 OK, 201 Created, 204 No Content, 404 Not Found, 409 Conflict.
Identity providers
Identity providers (IdPs) are the upstream OIDC providers the AS federates to during the authorization code flow.
GET /admin/api/identity-providers
Lists all IdPs.
| Query param | Description |
|---|---|
tenantId | Filter by tenant (optional) |
Response — array of IdP objects (see fields below).
GET /admin/api/identity-providers/{idpId}
Returns a single IdP by its idpId (provider key string).
POST /admin/api/identity-providers
Creates an IdP.
Request body:
| Field | Required | Type | Description |
|---|---|---|---|
issuer | Yes | string | OIDC issuer URL |
idpId | No | string | Provider key (auto-generated if omitted) |
name | No | string | Display name |
enabled | No | boolean | Default true |
clientId | No | string | Client ID at the upstream IdP |
clientSecret | No | string | Client secret at the upstream IdP |
scopes | No | string[] | Default scopes to request |
authorizationEndpoint | No | string | Override authorization endpoint |
tokenEndpoint | No | string | Override token endpoint |
userinfoEndpoint | No | string | Override userinfo endpoint |
jwksUri | No | string | Override JWKS URI |
tenantId | No | string | Tenant scoping |
Response — 201 Created, IdP object.
PUT /admin/api/identity-providers/{idpId}
Replaces an IdP. Same body as POST.
DELETE /admin/api/identity-providers/{idpId}
Deletes an IdP. 204 No Content on success.
curl -s -X POST https://pbac.example.com/admin/api/identity-providers \
-H "X-Admin-API-Key: dev-admin-key" \
-H "Content-Type: application/json" \
-d '{
"issuer": "https://idp.example.com",
"name": "Corporate IdP",
"clientId": "pbac-as",
"clientSecret": "secret",
"scopes": ["openid", "profile", "email"]
}'
Resource servers
Resource servers are the APIs that consume tokens issued by this AS and call /introspect to validate them.
GET /admin/api/resource-servers
Lists all resource servers.
| Query param | Description |
|---|---|
tenantId | Filter by tenant (optional) |
GET /admin/api/resource-servers/{id}
Returns a single resource server by internal numeric ID.
POST /admin/api/resource-servers
Creates a resource server.
Request body:
| Field | Required | Type | Description |
|---|---|---|---|
resourceIndicator | Yes | string | URI identifying the RS (e.g. https://api.example.com/fhir) |
name | No | string | Display name |
description | No | string | Free-text description |
capability | No | object | Arbitrary capability metadata (passed to OPA) |
clientId | No | integer | Internal PK of the client record used for introspection |
enabled | No | boolean | Default true |
typeUris | No | string[] | Resource type URIs served by this RS |
tenantId | No | string | Tenant scoping |
Response — 201 Created, resource server object.
PUT /admin/api/resource-servers/{id}
Replaces a resource server. Same body as POST.
DELETE /admin/api/resource-servers/{id}
Deletes a resource server. 204 No Content on success.
curl -s -X POST https://pbac.example.com/admin/api/resource-servers \
-H "X-Admin-API-Key: dev-admin-key" \
-H "Content-Type: application/json" \
-d '{
"resourceIndicator": "https://api.example.com/fhir",
"name": "FHIR API",
"typeUris": ["https://example.com/Patient"]
}'
Resource types
Resource types categorize protected resources and define the applicable scopes.
GET /admin/api/resource-types
Lists all resource types.
| Query param | Description |
|---|---|
tenantId | Filter by tenant (optional) |
GET /admin/api/resource-types/{id}
Returns a single resource type by internal numeric ID.
POST /admin/api/resource-types
Creates a resource type.
Request body:
| Field | Required | Type | Description |
|---|---|---|---|
typeUri | Yes | string | Type URI (e.g. https://example.com/Patient) |
name | No | string | Display name |
description | No | string | Free-text description |
parentTypeId | No | integer | Internal PK of parent type (for hierarchies) |
enabled | No | boolean | Default true |
scopes | No | string[] | Scope names applicable to this type |
tenantId | No | string | Tenant scoping |
Response — 201 Created, resource type object.
PUT /admin/api/resource-types/{id}
Replaces a resource type. Same body as POST.
DELETE /admin/api/resource-types/{id}
Deletes a resource type. 204 No Content on success.
curl -s -X POST https://pbac.example.com/admin/api/resource-types \
-H "X-Admin-API-Key: dev-admin-key" \
-H "Content-Type: application/json" \
-d '{
"typeUri": "https://example.com/Patient",
"name": "FHIR Patient",
"scopes": ["read", "write", "delete"]
}'
Resources
Resources are individual protected objects (e.g., a specific FHIR Patient record). Each resource belongs to a resource server and a resource type, and optionally has an owner subject.
GET /admin/api/resources
Lists all resources.
| Query param | Description |
|---|---|
tenantId | Filter by tenant (optional) |
GET /admin/api/resources/{id}
Returns a single resource by internal numeric ID.
POST /admin/api/resources
Creates a resource.
Request body:
| Field | Required | Type | Description |
|---|---|---|---|
resourceServerId | Yes | integer | Internal PK of the owning resource server |
resourceTypeId | Yes | integer | Internal PK of the resource type |
resourceIdentifier | Yes | string | URI or path that identifies this resource |
ownerSubjectId | No | integer | Internal PK of the owner subject |
scopes | No | string[] | Scope names assigned to this resource |
tenantId | No | string | Tenant scoping |
Response — 201 Created, resource object.
PUT /admin/api/resources/{id}
Replaces a resource. Same body as POST.
DELETE /admin/api/resources/{id}
Deletes a resource. 204 No Content on success.
curl -s -X POST https://pbac.example.com/admin/api/resources \
-H "X-Admin-API-Key: dev-admin-key" \
-H "Content-Type: application/json" \
-d '{
"resourceServerId": 1,
"resourceTypeId": 2,
"resourceIdentifier": "https://api.example.com/fhir/Patient/42",
"scopes": ["read"]
}'
Clients
Clients are OAuth 2.0 clients registered in the system. They can also be created via the dynamic registration endpoint (POST /register).
GET /admin/api/clients
Lists all clients.
| Query param | Description |
|---|---|
tenantId | Filter by tenant (optional) |
GET /admin/api/clients/{clientId}
Returns a single client by its clientId string.
| Query param | Description |
|---|---|
tenantId | Filter by tenant (optional) |
POST /admin/api/clients
Creates a client.
Request body:
| Field | Required | Type | Description |
|---|---|---|---|
clientId | Yes | string | Unique client identifier |
clientType | Yes | string | confidential or public |
clientSecret | No | string | BCrypt-hashed secret (confidential clients) |
enabled | No | boolean | Default true |
redirectUris | No | string[] | Allowed redirect URIs |
postLogoutRedirectUris | No | string[] | Post-logout redirect URIs |
clientName | No | string | Display name |
clientUri | No | string | Home page URI |
logoUri | No | string | Logo URI |
tokenEndpointAuthMethod | No | string | e.g. client_secret_basic, private_key_jwt |
jwksUri | No | string | HTTPS URI of the client's JWK Set |
jwks | No | object | Inline JWK Set |
softwareStatement | No | object | Software statement claims (JSON object) |
tenantId | No | string | Tenant scoping |
Note:
clientSecretmust be BCrypt-hashed before submission. The AS does not hash secrets submitted via the admin API (unlike DCR, which hashes automatically).
Response — 201 Created, client object.
PUT /admin/api/clients/{clientId}
Replaces a client. Same body as POST.
DELETE /admin/api/clients/{clientId}
Deletes a client. 204 No Content on success.
# Create a confidential client with a pre-hashed secret
curl -s -X POST https://pbac.example.com/admin/api/clients \
-H "X-Admin-API-Key: dev-admin-key" \
-H "Content-Type: application/json" \
-d '{
"clientId": "my-rs",
"clientType": "confidential",
"clientSecret": "$2a$10$...",
"softwareStatement": {
"granted_resources": [
{ "type": "urn:as:introspect", "scopes": ["uma_protection"] }
],
"resources": []
}
}'
Subjects
Subjects represent end-users (identified by their OIDC sub and IdP pair) in the policy model.
GET /admin/api/subjects
Lists all subjects.
| Query param | Description |
|---|---|
tenantId | Filter by tenant (optional) |
GET /admin/api/subjects/{id}
Returns a single subject by internal numeric ID.
POST /admin/api/subjects
Creates a subject.
Request body:
| Field | Required | Type | Description |
|---|---|---|---|
subjectId | Yes | string | OIDC sub claim value |
identityProviderId | No | integer | Internal PK of the associated IdP |
tenantId | No | string | Tenant scoping |
Response — 201 Created, subject object.
PUT /admin/api/subjects/{id}
Replaces a subject. Same body as POST.
DELETE /admin/api/subjects/{id}
Deletes a subject. 204 No Content on success.
curl -s -X POST https://pbac.example.com/admin/api/subjects \
-H "X-Admin-API-Key: dev-admin-key" \
-H "Content-Type: application/json" \
-d '{ "subjectId": "alice@example.com", "identityProviderId": 1 }'
Policy rules
Policy rules are Rego (or Cedar) policies stored in the database and served to OPA via the bundle endpoint. Changes take effect after OPA polls the next bundle.
GET /admin/api/policy-rules
Lists all policy rules.
| Query param | Description |
|---|---|
tenantId | Filter by tenant (optional) |
GET /admin/api/policy-rules/{id}
Returns a single policy rule by internal numeric ID.
POST /admin/api/policy-rules
Creates a policy rule.
Request body:
| Field | Required | Type | Description |
|---|---|---|---|
version | Yes | string | Version label (e.g. v1) |
kind | Yes | string | rego or cedar |
path | Yes | string | Rego package path (e.g. oauth/token) — becomes the file path in the bundle |
content | Yes | string | Policy source code |
enabled | No | boolean | Default true |
digest | No | string | Content digest for integrity checking |
tenantId | No | string | Tenant scoping |
Response — 201 Created, policy rule object.
PUT /admin/api/policy-rules/{id}
Replaces a policy rule by ID.
PUT /admin/api/policy-rules/by-path?path={path}
Upserts a policy rule by its path. Creates if absent, updates if present.
DELETE /admin/api/policy-rules/{id}
Deletes a policy rule. 204 No Content on success.
curl -s -X POST https://pbac.example.com/admin/api/policy-rules \
-H "X-Admin-API-Key: dev-admin-key" \
-H "Content-Type: application/json" \
-d '{
"version": "v1",
"kind": "rego",
"path": "oauth/token",
"content": "package oauth.token\n\ndefault allow = false\n\nallow {\n input.client.type == \"confidential\"\n}"
}'
Policy data
Policy data is JSON stored in the database and served to OPA as data.<key> keys in the bundle. Rows with no tenantId and no version are included in the shared OPA bundle.
GET /admin/api/policy-data
Lists all policy data entries.
| Query param | Type | Description |
|---|---|---|
tenantId | string | Filter by tenant (optional) |
bundledOnly | boolean | If true, return only entries included in the OPA bundle (default false) |
GET /admin/api/policy-data/{id}
Returns a single policy data entry by internal numeric ID.
POST /admin/api/policy-data
Creates a policy data entry.
Request body:
| Field | Required | Type | Description |
|---|---|---|---|
dataKey | Yes | string | OPA data key (e.g. oauth) — accessible as data.oauth in Rego |
payload | Yes | string | JSON string to store as the value |
version | No | string | Version label |
tenantId | No | string | Tenant scoping; entries with tenantId=null and version=null are bundled |
Response — 201 Created, policy data object. The inBundle field indicates whether this entry is included in the OPA bundle.
PUT /admin/api/policy-data/{id}
Replaces a policy data entry by ID.
PUT /admin/api/policy-data/by-key?dataKey={dataKey}
Upserts a policy data entry by its dataKey. Creates if absent, updates if present.
DELETE /admin/api/policy-data/{id}
Deletes a policy data entry. 204 No Content on success.
curl -s -X POST https://pbac.example.com/admin/api/policy-data \
-H "X-Admin-API-Key: dev-admin-key" \
-H "Content-Type: application/json" \
-d '{
"dataKey": "oauth",
"payload": "{\"resource\":{\"types\":{\"https://example.com/Patient\":{\"scopes\":[\"read\",\"write\"]}}}}"
}'
Audit logs
Audit logs are append-only records of AS operations. Read-only via the API.
GET /admin/api/audit-logs
Returns a paginated list of audit log entries.
| Query param | Description |
|---|---|
endpoint | Filter by endpoint path (e.g. /token) |
tenantId | Filter by tenant |
clientId | Filter by client ID |
page | Page number (0-indexed, default 0) |
size | Page size (default 20) |
sort | Sort field and direction (e.g. createdAt,desc) |
Response — 200 OK, Spring Page wrapper:
{
"content": [ ... ],
"totalElements": 42,
"totalPages": 3,
"number": 0,
"size": 20
}
Each entry:
| Field | Description |
|---|---|
id | Internal numeric ID |
endpoint | Request path |
method | HTTP method |
requestSummary | Key request fields (token presence, auth type, etc.) |
policyInput | OPA input that was evaluated |
policyDecision | OPA output |
outcome | allow, deny, or error |
tenantId | Tenant context |
clientId | Client that made the request |
subjectId | Subject (if applicable) |
ip | Client IP address |
userAgent | User-Agent header |
createdAt | ISO 8601 timestamp |
GET /admin/api/audit-logs/{id}
Returns a single audit log entry by internal numeric ID.
curl -s "https://pbac.example.com/admin/api/audit-logs?endpoint=/token&size=5&sort=createdAt,desc" \
-H "X-Admin-API-Key: dev-admin-key"
Next steps
- OAuth Endpoints Reference — Token, authorize, introspect, register, and AuthZEN endpoints
- Base Provisioning — Step-by-step guide to setting up IdPs, resource servers, and types
- Policy Data — Configure denylists, entitlements, and trust issuers via the policy data API