API reference

Operator view

The operator's own read of accounts, requests, messages and seats. Every route needs an administrator; an ordinary account is refused.

These rows are metadata: an address, dates, counts and statuses. No customer payload, no key material and no agreement text passes through them.

GET /v1/admin/accounts

Every account and its clock

One row per account: the address, when it was created, how many keys it holds, when the first key started its free period, when that period ends, and whether it has asked for continued access.

Auth
session cookie or API key
Capability
admin
Success
HTTP 200

Parameters

limit query, integer
Default 200.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/admin/accounts" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}

r = requests.get(f"{API}/v1/admin/accounts", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "accounts": [
    {
      "billing_status": "free_period",
      "created_at": "2026-09-01T08:00:00+00:00",
      "days_remaining": 12,
      "email": "ops@example.com",
      "first_key_at": "2026-09-07T09:14:02+00:00",
      "free_period_ends_at": "2026-10-07T09:14:02+00:00",
      "keys": 2,
      "last_call_at": "2026-09-19T22:41:07+00:00",
      "transition_request_status": null,
      "user_id": "2f6e1a0c-9b4d-4e8a-8c31-5d7f0a2b9c14"
    }
  ],
  "count": 1,
  "truncated": false
}

Errors

StatusCodeMeaning
401missing_token / invalid_or_expired_api_keyNo credential, an expired session, or a revoked or expired key.
403scope_missingA key limited by scopes lacks admin; the body names it.
403admin_onlyThe caller authenticated and is not an administrator.

What would show this is false

truncated says whether the walk stopped at the limit. A count that equals the limit with truncated false is the whole set; with truncated true it is a page, not a total.

GET /v1/admin/digest-preview

The operations digest, unsent

Return the digest content the scheduled send would produce, built by the same collector and renderer, without sending it.

Auth
session cookie or API key
Capability
admin
Success
HTTP 200

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/admin/digest-preview" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}

r = requests.get(f"{API}/v1/admin/digest-preview", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "counts": {
    "accounts_total": 41,
    "inquiries_in_window": 3
  },
  "recipient": "idgafholdingsllc@gmail.com",
  "sent": false,
  "subject": "AFA Protocol: operations digest, 2026-09-18 to 2026-09-20",
  "text": "Window: ...\n\nACCOUNTS AND KEYS\n  accounts, total        41\n..."
}

Errors

StatusCodeMeaning
401missing_token / invalid_or_expired_api_keyNo credential, an expired session, or a revoked or expired key.
403scope_missingA key limited by scopes lacks admin; the body names it.

What would show this is false

sent is false. A preview built by a second renderer would be a second place to be wrong, so this one calls the same two functions the scheduled send calls.

GET /v1/admin/inquiries

Messages sent to us

List what came in through POST /v1/inquiries, newest first.

Auth
session cookie or API key
Capability
admin
Success
HTTP 200

Parameters

limit query, integer
Default 200.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/admin/inquiries" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}

r = requests.get(f"{API}/v1/admin/inquiries", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "count": 1,
  "inquiries": [
    {
      "created_at": "2026-09-19T14:22:10+00:00",
      "email": "person@example.com",
      "id": "inq-3f9a1c74be205d68",
      "message": "We would like to try this on a nightly agent run.",
      "name": "A Person",
      "organisation": "Example Ltd",
      "source": "site"
    }
  ]
}

Errors

StatusCodeMeaning
401missing_token / invalid_or_expired_api_keyNo credential, an expired session, or a revoked or expired key.
403scope_missingA key limited by scopes lacks admin; the body names it.

What would show this is false

A submission that filled the hidden field never appears here. Send one and the count does not move.

GET /v1/admin/seats

The seat count, with its source

The same numbers GET /v1/seats serves, plus where the capacity came from.

Auth
session cookie or API key
Capability
admin
Success
HTTP 200

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/admin/seats" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}

r = requests.get(f"{API}/v1/admin/seats", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "capacity": 40,
  "note": "Seats are bounded by review capacity, not by infrastructure.",
  "remaining": 29,
  "source": "environment",
  "used": 11
}

Errors

StatusCodeMeaning
401missing_token / invalid_or_expired_api_keyNo credential, an expired session, or a revoked or expired key.
403scope_missingA key limited by scopes lacks admin; the body names it.

What would show this is false

source reads environment, default, or admin_override, and says which branch produced the number.

POST /v1/admin/seats

Raise or lower the seat count

Set the capacity for the life of this process. Not written to any store: a restart, a deploy or a second replica reads the durable setting again.

Auth
session cookie or API key
Capability
admin
Success
HTTP 200

Parameters

None.

Request body

capacity integer required
No description in the schema.

Example request

curl

curl -sS -X POST "https://api.afaprotocol.com/v1/admin/seats" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs" \
  -H "Content-Type: application/json" \
  -d '{
  "capacity": 60
}'

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}
payload = {
    "capacity": 60
}

r = requests.post(f"{API}/v1/admin/seats", headers=headers, json=payload, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "capacity": 60,
  "durable_setting": "AFA_BETA_SEATS",
  "note": "Seats are bounded by review capacity, not by infrastructure.",
  "persisted": false,
  "remaining": 49,
  "source": "admin_override",
  "status": "set",
  "used": 11
}

Errors

StatusCodeMeaning
401missing_token / invalid_or_expired_api_keyNo credential, an expired session, or a revoked or expired key.
403scope_missingA key limited by scopes lacks admin; the body names it.
422validation errorA required field is missing or a value has the wrong type.

What would show this is false

persisted is false and durable_setting names the variable to change instead. The operations digest reports the number it read, so a raise is on the record even though the value is not.

GET /v1/admin/transition-requests

Who has asked for continued access

List the requests, newest first, optionally narrowed to one status, with how many are not closed.

Auth
session cookie or API key
Capability
admin
Success
HTTP 200

Parameters

limit query, integer
Default 200.
status query, string or null
No description in the schema.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/admin/transition-requests" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}

r = requests.get(f"{API}/v1/admin/transition-requests", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "count": 1,
  "open_count": 1,
  "transition_requests": [
    {
      "admin_note": "",
      "contact_email": "ops@example.com",
      "email": "ops@example.com",
      "expected_monthly_calls": 50000,
      "notes": "",
      "organisation": "Example Ltd",
      "request_id": "tr-8c1f2a5b9d0e4f37",
      "status": "open",
      "submitted_at": "2026-09-20T11:02:44+00:00",
      "updated_at": "2026-09-20T11:02:44+00:00",
      "use_case": "attestation for a nightly agent run",
      "user_id": "2f6e1a0c-9b4d-4e8a-8c31-5d7f0a2b9c14"
    }
  ]
}

Errors

StatusCodeMeaning
401missing_token / invalid_or_expired_api_keyNo credential, an expired session, or a revoked or expired key.
403scope_missingA key limited by scopes lacks admin; the body names it.
422invalid_statusThe status filter must be open, in_progress or closed.

What would show this is false

Close one and open_count falls. The account's writes are still admitted: the gate reads whether an account asked, never what we did about it.

POST /v1/admin/transition-requests/{request_id}

Move one request along

Set a request to in_progress or closed, with a note.

Auth
session cookie or API key
Capability
admin
Success
HTTP 200

Parameters

request_id path, string required
No description in the schema.

Request body

note string
Default "".
status string required
No description in the schema.

Example request

curl

curl -sS -X POST "https://api.afaprotocol.com/v1/admin/transition-requests/request_id-example" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs" \
  -H "Content-Type: application/json" \
  -d '{
  "note": "call booked for Thursday",
  "status": "in_progress"
}'

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}
payload = {
    "note": "call booked for Thursday",
    "status": "in_progress"
}

r = requests.post(f"{API}/v1/admin/transition-requests/request_id-example", headers=headers, json=payload, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "status": "updated",
  "transition_request": {
    "admin_note": "call booked for Thursday",
    "contact_email": "ops@example.com",
    "email": "ops@example.com",
    "expected_monthly_calls": 50000,
    "notes": "",
    "organisation": "Example Ltd",
    "request_id": "tr-8c1f2a5b9d0e4f37",
    "status": "in_progress",
    "submitted_at": "2026-09-20T11:02:44+00:00",
    "updated_at": "2026-09-21T09:30:00+00:00",
    "use_case": "attestation for a nightly agent run",
    "user_id": "2f6e1a0c-9b4d-4e8a-8c31-5d7f0a2b9c14"
  }
}

Errors

StatusCodeMeaning
401missing_token / invalid_or_expired_api_keyNo credential, an expired session, or a revoked or expired key.
403scope_missingA key limited by scopes lacks admin; the body names it.
422validation errorA required field is missing or a value has the wrong type.
422invalid_statusOnly in_progress and closed can be set. open is the state a request is born in.
404no_such_requestThe id matches no stored request.

What would show this is false

An id that names nothing answers 404 with no_such_request, never a bare success.