API reference

Console data

The aggregates and views the console reads. They show what the API knows; nothing is inferred.

GET /v1/dashboard/alerts

Active banners

Lists the undismissed in-app alerts, newest first.

Auth
session cookie or API key
Capability
notifications
Success
HTTP 200

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/dashboard/alerts" \
  -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/dashboard/alerts", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "alerts": [
    {
      "body": "Sub-agent grant g-0c9f31ab7e2d revoked for 'worker-1'.",
      "created_at": "2026-09-01T14:20:00+00:00",
      "id": "c4d5e6f7-0a1b-4c2d-8e3f-9a0b1c2d3e4f",
      "kind": "subagent_grant_revoked",
      "severity": "info"
    }
  ]
}

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 notifications; the body names it.

What would show this is false

Dismiss an alert and list again: it is gone from this feed, while GET /v1/notifications keeps its dispatch row.

POST /v1/dashboard/alerts/{alert_id}/dismiss

Dismiss a banner

Removes one alert from the active feed.

Auth
session cookie or API key
Capability
notifications
Success
HTTP 204

Parameters

alert_id path, string required
No description in the schema.

Request body

None.

Example request

curl

curl -sS -X POST "https://api.afaprotocol.com/v1/dashboard/alerts/c4d5e6f7-0a1b-4c2d-8e3f-9a0b1c2d3e4f/dismiss" \
  -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.post(f"{API}/v1/dashboard/alerts/c4d5e6f7-0a1b-4c2d-8e3f-9a0b1c2d3e4f/dismiss", headers=headers, timeout=30)
r.raise_for_status()
print(r.status_code)  # 204, no body

Example response

HTTP 204
(no body)

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 notifications; the body names it.
422validation errorA required field is missing or a value has the wrong type.
404alert_not_foundNo active alert with this id on this account.

What would show this is false

Dismiss the same id twice: the second is 404. A dismissed banner cannot be dismissed again.

GET /v1/dashboard/dedup-stats

Dedup counters

Counts cache hits, reuses, misses and bypasses across the account's events.

Auth
session cookie or API key
Capability
dedup
Success
HTTP 200

The response also reports the cache window in events and seconds. These are counts of what the record says happened, not a measured saving.

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/dashboard/dedup-stats" \
  -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/dashboard/dedup-stats", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "by_action": {
    "execute": 5,
    "reuse": 1
  },
  "by_status": {
    "hit": 1,
    "miss": 2,
    "uncacheable": 3
  },
  "by_tool_hits": {
    "Read": 1
  },
  "bypasses": 0,
  "hit_rate": 0.333,
  "hits": 1,
  "misses": 2,
  "reuses": 1,
  "scanned": 6,
  "uncacheable": 3,
  "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 dedup; the body names it.

What would show this is false

hit_rate equals hits divided by hits plus misses, rounded to three places. bypasses and uncacheable are excluded from it.

GET /v1/dashboard/graph

Graph slice

Returns a cursor-paginated slice of events as nodes with parent edges, for the console's graph view.

Auth
session cookie or API key
Capability
events:read
Success
HTTP 200

Parameters

cursor query, integer or null
No description in the schema.
limit query, integer
Default 200.
machine query, string or null
No description in the schema.
tool query, string or null
No description in the schema.
types query, string or null
No description in the schema.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/dashboard/graph?limit=200" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs"

Python

import requests

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

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

Example response

HTTP 200
{
  "edges": [
    {
      "from": "env-7d3c1a9e5b2f4068",
      "kind": "parent",
      "to": "evt-5e6f708192a3b4c5"
    }
  ],
  "federation_root": "sha256:fa169e47437c116de26d2e8a91c3cd7c75c91c12cbe418cb3d60804008eb496c",
  "machine": null,
  "next_cursor": null,
  "nodes": [
    {
      "decision": "allow",
      "event_id": "evt-5e6f708192a3b4c5",
      "event_type": "tool_call",
      "machine_id": "worker-1",
      "machine_sequence": 3,
      "sequence": 4,
      "signature_status": "unsigned",
      "tool": "Read",
      "ts": "2026-09-01T14:10:21+00:00"
    }
  ],
  "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 events:read; the body names it.

What would show this is false

Pass machine=worker-1: federation_root is null and every node's machine_id is worker-1.

GET /v1/dashboard/machines

Machines and chain heads

Lists each machine's chain head and event count, plus the root that combines them.

Auth
session cookie or API key
Capability
events:read
Success
HTTP 200

Parameters

None.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/dashboard/machines" \
  -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/dashboard/machines", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())

Example response

HTTP 200
{
  "federation_root": "sha256:fa169e47437c116de26d2e8a91c3cd7c75c91c12cbe418cb3d60804008eb496c",
  "machines": [
    {
      "events_count": 5,
      "first_seen_at": "2026-08-31T14:12:07+00:00",
      "head_merkle": "sha256:bfe83410e270e861dc5c630ca25445b4e256181c4a4f43e0af5a7c53ed921e30",
      "head_seq": 5,
      "last_event_at": "2026-09-01T14:20:33+00:00",
      "machine_id": "worker-1",
      "revoked": false
    }
  ],
  "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 events:read; the body names it.

What would show this is false

Append one event for a machine: its head_seq and events_count rise by one and federation_root changes.

GET /v1/dashboard/usage

Event aggregates for a window

Counts the events ingested in a time window by type, tool and decision.

Auth
session cookie or API key
Capability
usage
Success
HTTP 200

This counts what was ingested. GET /v1/usage/summary counts what was called; they answer different questions.

Parameters

from query, string or null
No description in the schema.
to query, string or null
No description in the schema.

Request body

None.

Example request

curl

curl -sS -X GET "https://api.afaprotocol.com/v1/dashboard/usage?from=2026-08-25T00%3A00%3A00Z&to=2026-09-01T00%3A00%3A00Z" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs"

Python

import requests

API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}
params = {
    "from": "2026-08-25T00:00:00Z",
    "to": "2026-09-01T00:00:00Z"
}

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

Example response

HTTP 200
{
  "by_decision": {
    "allow": 6
  },
  "by_tool": {
    "Read": 1,
    "deploy": 4
  },
  "by_type": {
    "delegation_grant": 1,
    "tool_call": 3,
    "tool_result": 2
  },
  "total": 6,
  "user_id": "2f6e1a0c-9b4d-4e8a-8c31-5d7f0a2b9c14",
  "window": {
    "from": "2026-08-25T00:00:00+00:00",
    "to": "2026-09-01T00:00:00+00:00"
  }
}

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 usage; the body names it.
400invalid_timestamp / from_after_toA bound did not parse, or from is after to.

What would show this is false

total equals the count POST /v1/events/search returns for the same window.