Twenty minutes, one account

Quickstart

Five steps: sign in with an email code, create a key for one machine, write the first record, check it, then connect an agent host over MCP. Each step ends with what you can now show and how to check it.

1. Sign in

Open https://app.afaprotocol.com, enter your email and then the six digit code we send. The code works once. The session cookie lasts eight hours and is HttpOnly, SameSite=Strict, HTTPS only. The account is created on first sign-in; the beta terms are accepted there, and GET /v1/legal/documents serves the exact text with its hash.

Check: GET /v1/me with the cookie returns your email and a user id.

2. Create a key for one machine

In the console, open Keys, type a machine name such as worker-1, add a label if you want one, and press Create key. The token reads afa-beta-<26 characters>-<4-character check>, is shown once, and expires in 30 days. The check is computed from the 26 characters, so a token that lost one on the way through a shell or a chat window is refused with 401 malformed_key rather than reported as an unknown key. Keys issued before this format begin with ek-aa- and keep working until they expire. One key per machine, at most ten active. Add scopes to limit it to named capabilities. Only the session cookie can create, rotate or revoke keys; a key cannot mint another key.

curl -sS -X POST "https://api.afaprotocol.com/v1/me/api-keys" \
  -H "Cookie: afa_jwt=<session cookie>" -H "Content-Type: application/json" \
  -d '{"machine": "worker-1", "label": "deploy runner"}'

Check: GET /v1/me/api-keys lists the key with its prefix and expiry. The token itself is never listed.

3. Write the first record

The first event on an account has sequence 0 and prev_hash null. Every later event carries the payload_hash of the one before it and a higher sequence.

curl -sS -X POST "https://api.afaprotocol.com/v1/events" \
  -H "X-API-Key: afa-beta-EXAMPLE-e4qs" -H "Content-Type: application/json" \
  -d '{
  "decision": "allow",
  "event_id": "evt-1a2b3c4d5e6f7081",
  "event_type": "tool_call",
  "machine_id": "worker-1",
  "prev_hash": null,
  "raw": {
    "target": "staging",
    "ticket": "OPS-412"
  },
  "sequence": 0,
  "tool": "deploy"
}'

The response carries status: appended, the stored event with its payload_hash, and the quota used today. Post a second event with the wrong prev_hash and the API answers 409 prev_hash_mismatch with the value it expected.

Check: GET /v1/events/evt-1a2b3c4d5e6f7081 returns the event and its ancestry, without the raw text of anything.

4. Check the record

Fold the events into a settlement block, then verify the block chain.

curl -sS -X POST "https://api.afaprotocol.com/v1/chain/checkpoint" -H "X-API-Key: afa-beta-EXAMPLE-e4qs" -H "Content-Type: application/json" -d '{}'
curl -sS -X POST "https://api.afaprotocol.com/v1/chain/verify" -H "X-API-Key: afa-beta-EXAMPLE-e4qs"
HTTP 200
{"block_count": 1, "broken": [], "valid": true}

Check: the block's merkle_root recomputes from the sorted payload hashes of the events it covers. The recipe is on the Verify page.

5. Connect an agent host over MCP

claude mcp add --transport http afa https://api.afaprotocol.com/v1/mcp --header "X-API-Key: afa-beta-EXAMPLE-e4qs"

Other hosts are on the MCP page.

Check: the host lists afa_health among its tools and calling it returns storage: postgres.

What you can now show

An event id, its payload hash, the block that covers it, and the time it was written. What you still cannot show: the content of the action, or whether it was a good idea.