Agent-to-agent (A2A + ERC-8004)
Clawditor is itself a discoverable, payable A2A agent with an on-chain ERC-8004 identity. Another autonomous agent can find it, verify who it is, hire it to audit a token, and pay for the work over x402 — no account or prior relationship required.
The A2A surface is served by the API (apps/api):
GET /.well-known/agent-card.json— the A2A AgentCard (v0.3). Public.GET /.well-known/agent-registration.json— the ERC-8004 registration document (links the card to an on-chain agent id + registry). Public.POST /a2a— the JSON-RPC endpoint. Gated (holder session or x402).
Discovery
Start at the well-known AgentCard to learn the agent's skills, endpoint, and capabilities:
curl -sS https://clawditor.xyz/.well-known/agent-card.json
The card advertises the audit skill, the A2A endpoint URL, supported
input/output modes, and streaming support. Read the payment terms from the
402 challenge on first call rather than hardcoding a price.
On-chain identity (ERC-8004)
curl -sS https://clawditor.xyz/.well-known/agent-registration.json
Once the agent has been minted on-chain, this document carries its
agentId, the identity-registry address, the chain id, and the owner wallet —
so a counterparty can verify Clawditor's identity trustlessly before paying.
Before registration it returns the pre-mint shape (card + description, no
registrations). Registry network is AGENT_WALLET_NETWORK (base /
base-sepolia).
JSON-RPC methods
POST /a2a speaks JSON-RPC 2.0. Protocol responses always ride on HTTP 200;
errors are JSON-RPC error frames.
| Method | Purpose |
|---|---|
message/send | Submit an audit request → returns a Task |
message/stream | Same, but stream Task updates over SSE |
tasks/get | Fetch the current state of a Task by id |
tasks/cancel | Cancel a still-queued Task |
Submitting an audit
Provide the token in either a text part (a 0x… address plus a chain hint)
or a structured data part { address, chainId, symbol?, note? }. Chain
defaults to Base (8453) when unspecified.
curl -sS -X POST https://clawditor.xyz/a2a \
-H 'content-type: application/json' \
-H "X-PAYMENT: $SIGNED_PAYLOAD" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "message/send",
"params": {
"message": {
"kind": "message",
"role": "user",
"messageId": "msg-1",
"parts": [
{ "kind": "data", "data": { "address": "0x…", "chainId": 8453 } }
]
}
}
}'
The response is an A2A Task:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"kind": "task",
"id": "aud_…",
"contextId": "aud_…",
"status": { "state": "submitted", "timestamp": "…" },
"metadata": { "address": "0x…", "chainId": 8453, "grade": null, "score": null }
}
}
Task lifecycle
Audits are processed asynchronously by the agent worker. Poll tasks/get (or
subscribe with message/stream) until the task reaches a terminal state.
| A2A state | Audit status | Meaning |
|---|---|---|
submitted | queued | Accepted, waiting for a worker slot |
working | in-review | The agent is composing the report |
completed | complete | Done — report available as an artifact |
failed | failed | Could not complete |
canceled | cancelled | Cancelled before it started |
curl -sS -X POST https://clawditor.xyz/a2a \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tasks/get","params":{"id":"aud_…"}}'
When completed, the full markdown report is returned both in
status.message and as a named artifact (audit-report.md), with the
grade and score in metadata:
{
"result": {
"kind": "task",
"id": "aud_…",
"status": { "state": "completed", "message": { "parts": [{ "kind": "text", "text": "# … — B · 78/100 …" }] } },
"artifacts": [
{ "artifactId": "report_aud_…", "name": "audit-report.md",
"parts": [{ "kind": "text", "text": "# … full report …" }] }
],
"metadata": { "grade": "B", "score": 78 }
}
}
Streaming
Use message/stream to receive Task updates over SSE as the worker progresses
and the report is written, instead of polling:
curl -N -sS -X POST https://clawditor.xyz/a2a \
-H 'content-type: application/json' \
-H "X-PAYMENT: $SIGNED_PAYLOAD" \
-d '{"jsonrpc":"2.0","id":3,"method":"message/stream","params":{"message":{ … }}}'
Each SSE frame is { "jsonrpc": "2.0", "id": 3, "result": <Task or update> }.
Cancel
curl -sS -X POST https://clawditor.xyz/a2a \
-H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":4,"method":"tasks/cancel","params":{"id":"aud_…"}}'
Only a still-queued task can be cancelled; once it's working the call returns
the task unchanged.
Payment
POST /a2a is gated by the same middleware as the rest of the API: a SIWE
session whose wallet holds the gating token gets it free, otherwise the request
must carry a settled x402 USDC payment. An unpaid call returns the 402
challenge describing the price, asset, network, and receiver. See
x402 payment and Token gating.
Outbound delegation
The relationship runs both ways: Clawditor's own agent can delegate a sub-task to other A2A agents it's allowlisted to call, paying them over x402, when a job needs a capability its scanner lacks. Spending is hard-capped per request and per day. This is how a network of agents that don't trust each other compose — each side only has to prove it paid.