API reference
Chain heal
Scan the chain for breaks, gaps and signature failures, and append repair records. Repair is additive; nothing is edited or deleted.
POST/v1/heal/auto/startPOST/v1/heal/auto/stopPOST/v1/heal/runPOST/v1/heal/scanGET/v1/heal/status
POST /v1/heal/auto/start
Start the periodic scan
Starts a background scan of this account's chain every period_seconds inside the API process.
The task lives in the API process and does not survive a restart. GET /v1/heal/status reports auto_running, which is how you notice it stopped.
Parameters
None.
Request body
period_secondsinteger- Default
300.
Example request
curl
curl -sS -X POST "https://api.afaprotocol.com/v1/heal/auto/start" \
-H "X-API-Key: afa-beta-EXAMPLE-e4qs" \
-H "Content-Type: application/json" \
-d '{
"period_seconds": 300
}'
Python
import requests
API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}
payload = {
"period_seconds": 300
}
r = requests.post(f"{API}/v1/heal/auto/start", headers=headers, json=payload, timeout=30)
r.raise_for_status()
print(r.json())
Example response
HTTP 200
{
"period_seconds": 300,
"running": true,
"started_at": "2026-09-01T14:32:00+00:00",
"task_id": "heal-2f6e1a0c9b4d",
"user_id": "2f6e1a0c-9b4d-4e8a-8c31-5d7f0a2b9c14"
}
Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_token / invalid_or_expired_api_key | No credential, an expired session, or a revoked or expired key. |
| 403 | scope_missing | A key limited by scopes lacks governance; the body names it. |
| 422 | validation error | A required field is missing or a value has the wrong type. |
What would show this is false
Stop it and read status: auto_running false. A service restart also clears it, and status shows that too.
POST /v1/heal/auto/stop
Stop the periodic scan
Stops the background scan for this account.
Parameters
None.
Request body
None.
Example request
curl
curl -sS -X POST "https://api.afaprotocol.com/v1/heal/auto/stop" \
-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/heal/auto/stop", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())
Example response
HTTP 200
{
"running": false,
"started_at": "2026-09-01T14:32:00+00:00",
"stopped": true,
"stopped_at": "2026-09-01T15:02:00+00:00",
"task_id": "heal-2f6e1a0c9b4d",
"user_id": "2f6e1a0c-9b4d-4e8a-8c31-5d7f0a2b9c14"
}
Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_token / invalid_or_expired_api_key | No credential, an expired session, or a revoked or expired key. |
| 403 | scope_missing | A key limited by scopes lacks governance; the body names it. |
What would show this is false
Stop when nothing runs: stopped false, not an error. The response distinguishes the two cases.
POST /v1/heal/run
Append repair records
Appends one chain_repair event per finding at the tip of the chain, or only counts them when dry_run is true.
Repair is additive: no original event is modified or deleted. The repair record carries the diagnostic, so the break stays visible in the history.
Parameters
None.
Request body
dry_runboolean- Default
true. machine_idstring or null- No description in the schema.
Example request
curl
curl -sS -X POST "https://api.afaprotocol.com/v1/heal/run" \
-H "X-API-Key: afa-beta-EXAMPLE-e4qs" \
-H "Content-Type: application/json" \
-d '{
"dry_run": true,
"machine_id": "worker-1"
}'
Python
import requests
API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}
payload = {
"dry_run": True,
"machine_id": "worker-1"
}
r = requests.post(f"{API}/v1/heal/run", headers=headers, json=payload, timeout=30)
r.raise_for_status()
print(r.json())
Example response
HTTP 200
{
"actions": [],
"breaks": 0,
"completed_at": "2026-09-01T14:31:00+00:00",
"dry_run": true,
"gaps": 0,
"machine_id": "worker-1",
"merkle_root_status": "ok",
"signature_failures": 0,
"skipped": 0,
"user_id": "2f6e1a0c-9b4d-4e8a-8c31-5d7f0a2b9c14",
"would_write": 0,
"written": 0
}
Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_token / invalid_or_expired_api_key | No credential, an expired session, or a revoked or expired key. |
| 403 | scope_missing | A key limited by scopes lacks governance; the body names it. |
| 422 | validation error | A required field is missing or a value has the wrong type. |
What would show this is false
With dry_run true, written is 0 and a following scan reports the same total_events.
POST /v1/heal/scan
Scan the chain
Walks the account's events and reports breaks, gaps and signature failures without changing anything.
A break is a prev_hash that does not match the previous event's payload_hash. A gap is a missing sequence number. A signature failure is a signed event that does not verify against the machine's registered key.
Parameters
None.
Request body
machine_idstring or null- No description in the schema.
Example request
curl
curl -sS -X POST "https://api.afaprotocol.com/v1/heal/scan" \
-H "X-API-Key: afa-beta-EXAMPLE-e4qs" \
-H "Content-Type: application/json" \
-d '{
"machine_id": "worker-1"
}'
Python
import requests
API = "https://api.afaprotocol.com"
headers = {"X-API-Key": "afa-beta-EXAMPLE-e4qs"}
payload = {
"machine_id": "worker-1"
}
r = requests.post(f"{API}/v1/heal/scan", headers=headers, json=payload, timeout=30)
r.raise_for_status()
print(r.json())
Example response
HTTP 200
{
"breaks": [],
"clean": true,
"gaps": [],
"machine_id": "worker-1",
"merkle_root_status": "ok",
"scanned_at": "2026-09-01T14:30:00+00:00",
"signature_failures": [],
"total_events": 6,
"user_id": "2f6e1a0c-9b4d-4e8a-8c31-5d7f0a2b9c14",
"verified": 6
}
Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_token / invalid_or_expired_api_key | No credential, an expired session, or a revoked or expired key. |
| 403 | scope_missing | A key limited by scopes lacks governance; the body names it. |
| 422 | validation error | A required field is missing or a value has the wrong type. |
What would show this is false
clean is true only when all three lists are empty and merkle_root_status is ok. A root that cannot be verified reads mismatch, never a clean true.
GET /v1/heal/status
Last scan and repair
Returns the cached results of the last scan and repair for this account, and whether the periodic scan is running.
Parameters
None.
Request body
None.
Example request
curl
curl -sS -X GET "https://api.afaprotocol.com/v1/heal/status" \
-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/heal/status", headers=headers, timeout=30)
r.raise_for_status()
print(r.json())
Example response
HTTP 200
{
"auto_running": false,
"last_repair": null,
"last_scan": {
"breaks": [],
"clean": true,
"gaps": [],
"machine_id": "worker-1",
"merkle_root_status": "ok",
"scanned_at": "2026-09-01T14:30:00+00:00",
"signature_failures": [],
"total_events": 6,
"user_id": "2f6e1a0c-9b4d-4e8a-8c31-5d7f0a2b9c14",
"verified": 6
}
}
Errors
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_token / invalid_or_expired_api_key | No credential, an expired session, or a revoked or expired key. |
| 403 | scope_missing | A key limited by scopes lacks governance; the body names it. |
What would show this is false
Before any scan the response reads last_scan null. The cache is per account and per process; a restart clears it.