Admin API #
The admin API lets you manage lexicons, monitor records, run backfill jobs, and control admin access. All endpoints live under /admin and require an AIP-issued Bearer token from a DID that exists in the admins table. You can also manage all of this through the web dashboard.
Auth #
Admin auth works the same as user auth: the Bearer token is validated against AIP's /oauth/userinfo endpoint to retrieve the caller's DID. That DID is then checked against the admins table.
Auto-bootstrap: If the admins table is empty, the first authenticated request automatically inserts the caller as the initial admin.
Non-admin DIDs receive a 403 Forbidden response.
All error responses return JSON with an error field:
{
"error": "description of what went wrong"
}
| Status | Meaning |
|---|---|
400 Bad Request |
Invalid input (missing required fields, malformed lexicon JSON) |
401 Unauthorized |
Missing or invalid Bearer token. See AIP documentation for token issues |
403 Forbidden |
Authenticated DID is not in the admins table |
404 Not Found |
Lexicon, admin, or backfill job not found |
# All examples assume $TOKEN is an AIP-issued access token for an admin DID
AUTH="Authorization: Bearer $TOKEN"
Lexicons #
Upload / upsert a lexicon #
POST /admin/lexicons
curl -X POST http://localhost:3000/admin/lexicons \
-H "$AUTH" \
-H "Content-Type: application/json" \
-d '{
"lexicon_json": { "lexicon": 1, "id": "xyz.statusphere.status", "defs": { "main": { "type": "record", "key": "tid", "record": { "type": "object", "required": ["status", "createdAt"], "properties": { "status": { "type": "string", "maxGraphemes": 1 }, "createdAt": { "type": "string", "format": "datetime" } } } } } },
"backfill": true,
"target_collection": null
}'
| Field | Type | Required | Description |
|---|---|---|---|
lexicon_json |
object | yes | Raw lexicon JSON (must have lexicon: 1 and id) |
backfill |
boolean | no | Whether uploading triggers historical backfill (default true) |
target_collection |
string | no | For query/procedure lexicons, the record collection they operate on |
Response: 201 Created (new) or 200 OK (upsert)
{
"id": "xyz.statusphere.status",
"revision": 1
}
List lexicons #
GET /admin/lexicons
curl http://localhost:3000/admin/lexicons -H "$AUTH"
Response: 200 OK
[
{
"id": "xyz.statusphere.status",
"revision": 1,
"lexicon_type": "record",
"backfill": true,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
]
Get a lexicon #
GET /admin/lexicons/{id}
curl http://localhost:3000/admin/lexicons/xyz.statusphere.status -H "$AUTH"
Response: 200 OK with full lexicon details including raw JSON.
Delete a lexicon #
DELETE /admin/lexicons/{id}
curl -X DELETE http://localhost:3000/admin/lexicons/xyz.statusphere.status -H "$AUTH"
Response: 204 No Content
Network Lexicons #
Network lexicons are fetched from the AT Protocol network via DNS TXT resolution and kept updated via Tap. See Lexicons - Network lexicons for background.
Add a network lexicon #
POST /admin/network-lexicons
curl -X POST http://localhost:3000/admin/network-lexicons \
-H "$AUTH" \
-H "Content-Type: application/json" \
-d '{
"nsid": "xyz.statusphere.status",
"target_collection": null
}'
| Field | Type | Required | Description |
|---|---|---|---|
nsid |
string | yes | The NSID of the lexicon to watch |
target_collection |
string | no | For query/procedure lexicons, the record collection they operate on |
HappyView resolves the NSID authority via DNS TXT, fetches the lexicon from the authority's PDS, parses it, and stores it.
Response: 201 Created
{
"nsid": "xyz.statusphere.status",
"authority_did": "did:plc:authority",
"revision": 1
}
List network lexicons #
GET /admin/network-lexicons
curl http://localhost:3000/admin/network-lexicons -H "$AUTH"
Response: 200 OK
[
{
"nsid": "xyz.statusphere.status",
"authority_did": "did:plc:authority",
"target_collection": null,
"last_fetched_at": "2025-01-01T00:00:00Z",
"created_at": "2025-01-01T00:00:00Z"
}
]
Remove a network lexicon #
DELETE /admin/network-lexicons/{nsid}
curl -X DELETE http://localhost:3000/admin/network-lexicons/xyz.statusphere.status \
-H "$AUTH"
Removes the network lexicon tracking and also deletes the lexicon from the lexicons table and in-memory registry.
Response: 204 No Content
Stats #
Record counts #
GET /admin/stats
curl http://localhost:3000/admin/stats -H "$AUTH"
Response: 200 OK
{
"total_records": 12345,
"collections": [{ "collection": "xyz.statusphere.status", "count": 500 }]
}
Tap Stats #
Aggregate stats from the Tap instance. Useful for monitoring backfill progress. See Backfill - Job lifecycle for context.
Get Tap stats #
GET /admin/tap/stats
curl http://localhost:3000/admin/tap/stats -H "$AUTH"
Response: 200 OK
{
"repo_count": 5234,
"record_count": 1048576,
"outbox_buffer": 42
}
| Field | Type | Description |
|---|---|---|
repo_count |
number | Total repos Tap is tracking |
record_count |
number | Total records Tap has indexed |
outbox_buffer |
number | Pending events awaiting delivery (high = Tap is busy) |
Returns 502 Bad Gateway if Tap is unreachable.
Backfill #
Create a backfill job #
POST /admin/backfill
curl -X POST http://localhost:3000/admin/backfill \
-H "$AUTH" \
-H "Content-Type: application/json" \
-d '{ "collection": "xyz.statusphere.status" }'
| Field | Type | Required | Description |
|---|---|---|---|
collection |
string | no | Limit to a single collection (backfills all if omitted) |
did |
string | no | Limit to a single DID (discovers all via relay if omitted) |
Response: 201 Created
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "pending"
}
List backfill jobs #
GET /admin/backfill/status
curl http://localhost:3000/admin/backfill/status -H "$AUTH"
Response: 200 OK
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"collection": "xyz.statusphere.status",
"did": null,
"status": "completed",
"total_repos": 42,
"processed_repos": 42,
"total_records": 1000,
"error": null,
"started_at": "2025-01-01T00:01:00Z",
"completed_at": "2025-01-01T00:05:00Z",
"created_at": "2025-01-01T00:00:00Z"
}
]
Admin management #
Add an admin #
POST /admin/admins
curl -X POST http://localhost:3000/admin/admins \
-H "$AUTH" \
-H "Content-Type: application/json" \
-d '{ "did": "did:plc:newadmin" }'
Response: 201 Created
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"did": "did:plc:newadmin"
}
List admins #
GET /admin/admins
curl http://localhost:3000/admin/admins -H "$AUTH"
Response: 200 OK
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"did": "did:plc:admin",
"created_at": "2025-01-01T00:00:00Z",
"last_used_at": "2025-01-02T12:00:00Z"
}
]
Remove an admin #
DELETE /admin/admins/{id}
curl -X DELETE http://localhost:3000/admin/admins/550e8400-e29b-41d4-a716-446655440000 \
-H "$AUTH"
Response: 204 No Content