API reference

Operator cron

Operator-only cron invokers behind a shared secret. Not a customer surface; documented so their effects on your account can be explained.

POST /v1/internal/cron/admin-digest

Operations digest

Sends one plain-text summary of the last 48 hours to the operator's own address: accounts and keys created, calls by capability, notifications by type, grants created and revoked, governance transitions, and what is queued. Not a customer notification, and no customer preference reaches it.

Auth
X-Cron-Secret (operator only)
Capability
none needed (cron invokers, gated by X-Cron-Secret not by a key)
Success
HTTP 200

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X POST "https://api.afaprotocol.com/v1/internal/cron/admin-digest" \
  -H "X-Cron-Secret: <operator secret>" \
  -H "Content-Type: application/json" \
  -d '{}'

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-Cron-Secret": "<operator secret>"}
payload = {}

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

Example response

HTTP 200
{
  "counts": {
    "accounts_new": 2,
    "accounts_total": 12,
    "calls_by_capability": {
      "events:write": 480,
      "grants": 26
    },
    "calls_total": 506,
    "deliveries_failed": 0,
    "errors": null,
    "errors_status": "not recorded",
    "grants_created": 4,
    "grants_revoked": 1,
    "keys_created": 3,
    "mcp_calls": null,
    "mcp_calls_status": "not recorded",
    "notifications_total": 9,
    "since": "2026-09-05T00:00:00+00:00",
    "until": "2026-09-07T00:00:00+00:00",
    "webhook_queue_pending_now": 0,
    "window_hours": 48
  },
  "delivery_status": "sent",
  "provider": "resend",
  "recipient": "operator address"
}

Errors

StatusCodeMeaning
401 / 503unauthorized / cron_secret_unconfiguredWrong shared secret, or the deployment has none configured.
401unauthorizedThe shared secret header is absent or does not match.
503cron_secret_unconfiguredNo secret is configured. The endpoint refuses rather than running open.

What would show this is false

Three counts read 'not recorded' rather than zero: calls arriving over the tool transport specifically, application errors, and webhook failures across the window. Nothing stores those, and a zero would be a claim about the world where the truth is a claim about the records. Call counts are summed over whole days, and the response names the days it summed.

POST /v1/internal/cron/anchor

Anchor every changed root

Anchors each account whose root moved since its last receipt; unchanged roots are skipped.

Auth
X-Cron-Secret (operator only)
Capability
none needed (cron invokers, gated by X-Cron-Secret not by a key)
Success
HTTP 200

Operator-only. With the provider set to none the sweep reports anchoring_disabled and visits nobody; a bad provider name reports provider_misconfigured with the error.

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X POST "https://api.afaprotocol.com/v1/internal/cron/anchor" \
  -H "X-Cron-Secret: <operator secret>"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-Cron-Secret": "<operator secret>"}

r = requests.post(f"{API}/v1/internal/cron/anchor", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "swept": {
    "anchor_failed": 0,
    "anchored": 2,
    "no_root": 0,
    "provider": "rekor",
    "status": "ok",
    "unchanged": 16,
    "users": 18
  }
}

Errors

StatusCodeMeaning
401 / 503unauthorized / cron_secret_unconfiguredWrong shared secret, or the deployment has none configured.
401unauthorizedWrong or missing shared secret.
503cron_secret_unconfiguredThe deployment has no secret; it fails closed rather than open.

What would show this is false

Run twice with no new events: the second reports every visited account as unchanged.

POST /v1/internal/cron/digest

Run the daily digest

Rolls each account's digest-eligible email notices from the last 24 hours into one email.

Auth
X-Cron-Secret (operator only)
Capability
none needed (cron invokers, gated by X-Cron-Secret not by a key)
Success
HTTP 200

Operator-only. Runs once a day at 14:00 UTC.

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X POST "https://api.afaprotocol.com/v1/internal/cron/digest" \
  -H "X-Cron-Secret: <operator secret>"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-Cron-Secret": "<operator secret>"}

r = requests.post(f"{API}/v1/internal/cron/digest", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "swept": {
    "digests_sent": 2,
    "grouped": {
      "rate.limit_hit": 5
    },
    "users": 18
  }
}

Errors

StatusCodeMeaning
401 / 503unauthorized / cron_secret_unconfiguredWrong shared secret, or the deployment has none configured.
401unauthorizedWrong or missing shared secret.
503cron_secret_unconfiguredThe deployment has no secret; it fails closed rather than open.

What would show this is false

Run twice: the second reports digests_sent 0 for accounts the first already rolled up.

POST /v1/internal/cron/grant-expiry

Announce grants expiring within 24 hours

Dispatches grant.expiring_24h for each live sub-agent grant that expires within a day.

Auth
X-Cron-Secret (operator only)
Capability
none needed (cron invokers, gated by X-Cron-Secret not by a key)
Success
HTTP 200

Operator-only. Runs every six hours. Expiry itself is still evaluated at the gate; this only announces it early.

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X POST "https://api.afaprotocol.com/v1/internal/cron/grant-expiry" \
  -H "X-Cron-Secret: <operator secret>"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-Cron-Secret": "<operator secret>"}

r = requests.post(f"{API}/v1/internal/cron/grant-expiry", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "swept": {
    "expiring": 1,
    "notified": 1,
    "users": 18
  }
}

Errors

StatusCodeMeaning
401 / 503unauthorized / cron_secret_unconfiguredWrong shared secret, or the deployment has none configured.
401unauthorizedWrong or missing shared secret.
503cron_secret_unconfiguredThe deployment has no secret; it fails closed rather than open.

What would show this is false

A retried invocation re-fires nothing: the notice is idempotent per grant and expiry through the dispatch log.

POST /v1/internal/cron/key-expiry

Sweep expiring keys

Warns each account whose key expires within 7 days or 24 hours, once per key and window.

Auth
X-Cron-Secret (operator only)
Capability
none needed (cron invokers, gated by X-Cron-Secret not by a key)
Success
HTTP 200

Operator-only. Not a customer surface; listed so the key.expiring notices on your account can be explained.

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X POST "https://api.afaprotocol.com/v1/internal/cron/key-expiry" \
  -H "X-Cron-Secret: <operator secret>"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-Cron-Secret": "<operator secret>"}

r = requests.post(f"{API}/v1/internal/cron/key-expiry", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "swept": {
    "scanned": 12,
    "warned_24h": 0,
    "warned_7d": 1
  }
}

Errors

StatusCodeMeaning
401 / 503unauthorized / cron_secret_unconfiguredWrong shared secret, or the deployment has none configured.
401unauthorizedWrong or missing shared secret.
503cron_secret_unconfiguredThe deployment has no secret; it fails closed rather than open.

What would show this is false

A repeat invocation reports zero new warnings, because the dispatch log already holds each key and window.

POST /v1/internal/cron/webhooks

Drain the webhook queue

Posts each queued payload to its endpoint with bounded retry and records sent or failed.

Auth
X-Cron-Secret (operator only)
Capability
none needed (cron invokers, gated by X-Cron-Secret not by a key)
Success
HTTP 200

Operator-only. Runs every two minutes. Only a 2xx counts as sent; after three failures an item is recorded failed and dropped.

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X POST "https://api.afaprotocol.com/v1/internal/cron/webhooks" \
  -H "X-Cron-Secret: <operator secret>"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-Cron-Secret": "<operator secret>"}

r = requests.post(f"{API}/v1/internal/cron/webhooks", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "webhooks": {
    "drained": 3,
    "failed": 1,
    "sent": 2
  }
}

Errors

StatusCodeMeaning
401 / 503unauthorized / cron_secret_unconfiguredWrong shared secret, or the deployment has none configured.
401unauthorizedWrong or missing shared secret.
503cron_secret_unconfiguredThe deployment has no secret; it fails closed rather than open.

What would show this is false

After a drain, GET /v1/notifications on the affected account shows the webhook rows as sent or failed, not queued.