Skip to main content

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.

MethodPurpose
message/sendSubmit an audit request → returns a Task
message/streamSame, but stream Task updates over SSE
tasks/getFetch the current state of a Task by id
tasks/cancelCancel 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 stateAudit statusMeaning
submittedqueuedAccepted, waiting for a worker slot
workingin-reviewThe agent is composing the report
completedcompleteDone — report available as an artifact
failedfailedCould not complete
canceledcancelledCancelled 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.