Skip to content

API Reference

API Reference

All admin endpoints are mounted under /admin/system/ and require a valid Clerk JWT with publicMetadata.role === 'admin'. Each endpoint additionally requires a specific permission resolved from the caller’s admin role in ADMIN_DB.

Base URL: https://your-worker.workers.dev

Authentication: All requests require Authorization: Bearer <clerk_jwt>.


Roles

List Roles

GET /admin/system/roles

Permission: admin:read

Response 200:

{
"success": true,
"roles": [
{
"id": 1,
"role_name": "viewer",
"display_name": "Viewer",
"description": "Read-only access to admin dashboards and logs",
"permissions": "[\"admin:read\",\"audit:read\",\"metrics:read\",\"config:read\",\"users:read\",\"flags:read\"]",
"is_active": 1,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-01-01T00:00:00"
}
]
}

Create Role

POST /admin/system/roles

Permission: roles:write

Request Body:

FieldTypeRequiredDescription
role_namestringUnique role identifier (e.g. flag-manager)
display_namestringHuman-readable name
descriptionstringRole description
permissionsstring[]Array of permission strings (min 1)
Terminal window
curl -X POST /admin/system/roles \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"role_name": "flag-manager",
"display_name": "Flag Manager",
"description": "Manages feature flags only",
"permissions": ["admin:read", "flags:read", "flags:write"]
}'

Response 201:

{
"success": true,
"role": { "id": 4, "role_name": "flag-manager", "..." : "..." }
}

Update Role

PATCH /admin/system/roles/:id

Permission: roles:write

Path Parameters: id — Role ID (integer)

Request Body (all fields optional):

FieldTypeDescription
display_namestringUpdated display name
descriptionstringUpdated description
permissionsstring[]Replace permission array (min 1)
is_activebooleanEnable/disable the role

Response 200:

{
"success": true,
"role": { "id": 4, "role_name": "flag-manager", "..." : "..." }
}

List Role Assignments

GET /admin/system/roles/assignments

Permission: admin:read

Query Parameters (all optional):

ParamTypeDescription
clerk_user_idstringFilter by user
role_namestringFilter by role
Terminal window
curl "/admin/system/roles/assignments?role_name=editor" \
-H "Authorization: Bearer $JWT"

Response 200:

{
"success": true,
"assignments": [
{
"id": 1,
"clerk_user_id": "user_2abc123",
"role_name": "editor",
"assigned_by": "user_2xyz789",
"assigned_at": "2025-01-15T10:30:00",
"expires_at": null
}
]
}

Assign Role

POST /admin/system/roles/assign

Permission: roles:assign

Request Body:

FieldTypeRequiredDescription
clerk_user_idstringClerk user ID to assign the role to
role_namestringRole to assign (viewer, editor, super-admin, or custom)
expires_atstringISO 8601 expiry date (null = never expires)
Terminal window
curl -X POST /admin/system/roles/assign \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"clerk_user_id": "user_2abc123",
"role_name": "editor",
"expires_at": "2025-12-31T23:59:59Z"
}'

Response 200:

{
"success": true,
"assignment": {
"clerk_user_id": "user_2abc123",
"role_name": "editor",
"assigned_by": "user_2xyz789",
"expires_at": "2025-12-31T23:59:59Z"
}
}

Uses INSERT ... ON CONFLICT ... DO UPDATE — re-assigning updates the existing assignment.


Revoke Role

DELETE /admin/system/roles/revoke

Permission: roles:assign

Request Body:

FieldTypeRequiredDescription
clerk_user_idstringUser to revoke from
role_namestringRole to revoke
Terminal window
curl -X DELETE /admin/system/roles/revoke \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"clerk_user_id": "user_2abc123",
"role_name": "editor"
}'

Response 200:

{ "success": true, "message": "Role revoked" }

KV cache for the user is invalidated automatically.


My Context

Get My Context

GET /admin/system/my-context

Permission: None required (returns the caller’s own context)

Returns the full dashboard context for the authenticated admin, including role, permissions, tiers, scopes, flags, and active announcements.

Terminal window
curl /admin/system/my-context -H "Authorization: Bearer $JWT"

Response 200:

{
"success": true,
"context": {
"clerk_user_id": "user_2abc123",
"role_name": "super-admin",
"permissions": ["admin:read", "admin:write", "..."],
"expires_at": null
}
}

Get My Permissions

GET /admin/system/my-permissions

Permission: None required

Returns only the caller’s resolved permissions array.


Tiers

List Tiers

GET /admin/system/tiers

Permission: admin:read

Response 200:

{
"success": true,
"tiers": [
{
"id": 1,
"tier_name": "anonymous",
"order_rank": 0,
"rate_limit": 10,
"display_name": "Anonymous",
"description": "Unauthenticated user — basic access",
"features": "{\"maxSources\": 3, \"maxBatchSize\": 1}",
"is_active": 1,
"created_at": "2025-01-01T00:00:00",
"updated_at": "2025-01-01T00:00:00"
}
]
}

Update Tier

PUT /admin/system/tiers/:name

Permission: config:write

Path Parameters: name — Tier name (e.g. free, pro)

Request Body (all fields optional):

FieldTypeDescription
rate_limitnumberRequests per minute (0 = unlimited)
display_namestringHuman-readable name
descriptionstringTier description
featuresobjectJSON feature flags/capabilities
is_activebooleanEnable/disable the tier
Terminal window
curl -X PUT /admin/system/tiers/pro \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"rate_limit": 500,
"features": {"maxSources": 100, "maxBatchSize": 50, "priorityQueue": true}
}'

Response 200:

{ "success": true, "tier": { "tier_name": "pro", "..." : "..." } }

Delete Tier

DELETE /admin/system/tiers/:name

Permission: config:write

Path Parameters: name — Tier name

Response 200:

{ "success": true, "message": "Tier deleted" }

Scopes

List Scopes

GET /admin/system/scopes

Permission: admin:read

Response 200:

{
"success": true,
"scopes": [
{
"id": 1,
"scope_name": "compile",
"display_name": "Compile",
"description": "Compile and download filter lists",
"required_tier": "free",
"is_active": 1
}
]
}

Update Scope

PUT /admin/system/scopes/:name

Permission: config:write

Path Parameters: name — Scope name (e.g. compile, rules)

Request Body (all fields optional):

FieldTypeDescription
display_namestringHuman-readable name
descriptionstringScope description
required_tierstringMinimum tier required (anonymous, free, pro, admin)
is_activebooleanEnable/disable the scope
Terminal window
curl -X PUT /admin/system/scopes/compile \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{ "required_tier": "pro" }'

Delete Scope

DELETE /admin/system/scopes/:name

Permission: config:write


Endpoint Auth Overrides

Per-endpoint authentication requirements that override the global defaults.

List Endpoint Overrides

GET /admin/system/endpoints

Permission: admin:read

Response 200:

{
"success": true,
"endpoints": [
{
"id": 1,
"path_pattern": "/api/rules/*",
"method": "*",
"required_tier": "pro",
"required_scopes": "[\"rules\"]",
"is_public": 0,
"is_active": 1
}
]
}

Create Endpoint Override

POST /admin/system/endpoints

Permission: config:write

Request Body:

FieldTypeRequiredDescription
path_patternstringURL pattern (e.g. /compile, /api/rules/*)
methodstringHTTP method or * for all (default: *)
required_tierstringTier override (null = use default)
required_scopesstring[]Scope overrides (null = use default)
is_publicbooleantrue = no auth required
Terminal window
curl -X POST /admin/system/endpoints \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"path_pattern": "/api/public/health",
"method": "GET",
"is_public": true
}'

Update Endpoint Override

PUT /admin/system/endpoints/:id

Permission: config:write

Path Parameters: id — Override ID (integer)

Request Body (all fields optional):

FieldTypeDescription
required_tierstringUpdated tier requirement
required_scopesstring[]Updated scope requirements
is_publicbooleanToggle public access
is_activebooleanEnable/disable the override

Delete Endpoint Override

DELETE /admin/system/endpoints/:id

Permission: config:write


Feature Flags

List Flags

GET /admin/system/flags

Permission: admin:read

Response 200:

{
"success": true,
"flags": [
{
"id": 1,
"flag_name": "new-parser-v2",
"enabled": 1,
"rollout_percentage": 25,
"target_tiers": "[\"pro\",\"admin\"]",
"target_users": "[]",
"description": "New AGTree v2 parser",
"created_by": "user_2abc123",
"created_at": "2025-01-15T10:30:00",
"updated_at": "2025-01-15T10:30:00"
}
]
}

Create Flag

POST /admin/system/flags

Permission: flags:write

Request Body:

FieldTypeRequiredDescription
flag_namestringUnique flag identifier
enabledbooleanDefault: false
rollout_percentagenumber0–100, default: 100
target_tiersstring[]Tier filter (empty = all tiers)
target_usersstring[]Clerk user IDs for user-level targeting
descriptionstringHuman-readable description
Terminal window
curl -X POST /admin/system/flags \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"flag_name": "streaming-api-beta",
"enabled": true,
"rollout_percentage": 10,
"target_tiers": ["pro", "admin"],
"description": "Server-sent events streaming API"
}'

Update Flag

PATCH /admin/system/flags/:id

Permission: flags:write

Path Parameters: id — Flag ID (integer)

Request Body: Same fields as create, all optional.


Delete Flag

DELETE /admin/system/flags/:id

Permission: flags:write


Audit Logs

Query Audit Logs

GET /admin/system/audit

Permission: audit:read

Query Parameters (all optional):

ParamTypeDefaultDescription
actor_idstringFilter by Clerk user ID
actionstringFilter by action (e.g. tier.update)
resource_typestringFilter by resource type (e.g. feature_flag)
resource_idstringFilter by specific resource ID
statusstringsuccess, failure, or denied
sincestringISO 8601 start date
untilstringISO 8601 end date
limitnumber50Results per page (max 100)
offsetnumber0Pagination offset
Terminal window
curl "/admin/system/audit?action=tier.update&since=2025-01-01T00:00:00Z&limit=20" \
-H "Authorization: Bearer $JWT"

Response 200:

{
"success": true,
"logs": [
{
"id": 42,
"actor_id": "user_2abc123",
"actor_email": "admin@example.com",
"action": "tier.update",
"resource_type": "tier_config",
"resource_id": "pro",
"old_values": "{\"rate_limit\": 300}",
"new_values": "{\"rate_limit\": 500}",
"ip_address": "203.0.113.1",
"user_agent": "Mozilla/5.0...",
"status": "success",
"metadata": null,
"created_at": "2025-01-15T10:30:00"
}
],
"total": 142,
"limit": 20,
"offset": 0
}

Announcements

List Announcements

GET /admin/system/announcements

Permission: admin:read

Response 200:

{
"success": true,
"announcements": [
{
"id": 1,
"title": "Scheduled Maintenance",
"body": "The platform will be briefly unavailable on Jan 20 from 02:00-04:00 UTC.",
"severity": "warning",
"active_from": "2025-01-18T00:00:00",
"active_until": "2025-01-20T04:00:00",
"is_active": 1,
"created_by": "user_2abc123",
"created_at": "2025-01-15T10:30:00",
"updated_at": "2025-01-15T10:30:00"
}
]
}

Create Announcement

POST /admin/system/announcements

Permission: config:write

Request Body:

FieldTypeRequiredDescription
titlestringAnnouncement title
bodystringAnnouncement body (supports markdown)
severitystringinfo, warning, error, or success (default: info)
active_fromstringISO 8601 start time (null = immediately)
active_untilstringISO 8601 end time (null = no expiry)
Terminal window
curl -X POST /admin/system/announcements \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"title": "New Parser Available",
"body": "AGTree v2 is now available for all Pro users.",
"severity": "success",
"active_until": "2025-02-01T00:00:00Z"
}'

Update Announcement

PATCH /admin/system/announcements/:id

Permission: config:write

Path Parameters: id — Announcement ID (integer)

Request Body: Same fields as create, all optional. Additionally:

FieldTypeDescription
is_activebooleanEnable/disable the announcement

Delete Announcement

DELETE /admin/system/announcements/:id

Permission: config:write


Local Auth Users

CRUD endpoints for managing local JWT auth users. These endpoints are available when the Worker is configured with a D1 DB binding and JWT_SECRET. They mirror Clerk’s Backend API for easy migration.

Authentication required: Admin tier + admin role (enforced by checkRoutePermission).

Note: Password hashes are never returned. The api_disabled flag can be toggled to block a user’s API access without deleting the account.


List Local Auth Users

GET /admin/local-users

Query Parameters:

ParameterTypeDefaultDescription
limitinteger50Max results per page (1–100)
offsetinteger0Pagination offset

Response:

{
"success": true,
"users": [
{
"id": "uuid",
"identifier": "user@example.com",
"identifier_type": "email",
"role": "user",
"tier": "free",
"api_disabled": 0,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
],
"total": 42,
"limit": 50,
"offset": 0
}

Get Local Auth User

GET /admin/local-users/:id

Path Parameters: id — User UUID

Response: { "success": true, "user": { ... } } — same user object shape as the list items above.


Create Local Auth User

POST /admin/local-users

Request Body:

FieldTypeRequiredDescription
identifierstringEmail or phone number
passwordstringPlaintext password (hashed server-side with PBKDF2)
rolestringuser or admin (default: user)
tierstringOverride tier; defaults to the role’s default tier

Response: 201 with the created user object.

Errors:

  • 409 Conflict — identifier already registered

Update Local Auth User

PATCH /admin/local-users/:id

Request Body (at least one field required):

FieldTypeDescription
rolestringNew role (user or admin)
tierstringNew tier (free, pro, admin)
api_disabled0 | 11 blocks all API access for this user

Tier and role are independent. Setting role without tier auto-derives the default tier for that role. Setting both allows overrides (e.g. { "role": "user", "tier": "pro" } for a Pro-rate-limited non-admin user).

Response: Updated user object.


Delete Local Auth User

DELETE /admin/local-users/:id

Response: { "success": true, "message": "User deleted" }

Errors:

  • 404 Not Found — user does not exist

API Usage

Query per-user API usage statistics stored in KV. Useful for monitoring, abuse detection, and billing.

Authentication required: Admin tier + admin role.


Get User Usage

GET /admin/usage/:userId

Path Parameters: userId — User UUID (local auth user ID)

Query Parameters:

ParameterTypeDefaultDescription
daysinteger30Lookback window in days (1–90)

Response:

{
"success": true,
"userId": "uuid",
"total": {
"count": 1234,
"firstSeen": "2025-01-01T00:00:00Z",
"lastSeen": "2025-03-15T12:00:00Z"
},
"days": [
{ "date": "2025-03-15", "count": 42, "routes": { "/compile": 38, "/validate": 4 } }
],
"lookbackDays": 30
}

Endpoint Summary

#MethodPathPermission
1GET/admin/system/rolesadmin:read
2POST/admin/system/rolesroles:write
3PATCH/admin/system/roles/:idroles:write
4GET/admin/system/roles/assignmentsadmin:read
5POST/admin/system/roles/assignroles:assign
6DELETE/admin/system/roles/revokeroles:assign
7GET/admin/system/my-context(none)
8GET/admin/system/my-permissions(none)
9GET/admin/system/tiersadmin:read
10PUT/admin/system/tiers/:nameconfig:write
11DELETE/admin/system/tiers/:nameconfig:write
12GET/admin/system/scopesadmin:read
13PUT/admin/system/scopes/:nameconfig:write
14DELETE/admin/system/scopes/:nameconfig:write
15GET/admin/system/endpointsadmin:read
16POST/admin/system/endpointsconfig:write
17PUT/admin/system/endpoints/:idconfig:write
18DELETE/admin/system/endpoints/:idconfig:write
19GET/admin/system/flagsadmin:read
20POST/admin/system/flagsflags:write
21PATCH/admin/system/flags/:idflags:write
22DELETE/admin/system/flags/:idflags:write
23GET/admin/system/auditaudit:read
24GET/admin/system/announcementsadmin:read
25POST/admin/system/announcementsconfig:write
26PATCH/admin/system/announcements/:idconfig:write
27DELETE/admin/system/announcements/:idconfig:write
28GET/admin/local-usersAdmin role
29GET/admin/local-users/:idAdmin role
30POST/admin/local-usersAdmin role
31PATCH/admin/local-users/:idAdmin role
32DELETE/admin/local-users/:idAdmin role
33GET/admin/usage/:userIdAdmin role