Protocol surfaces
A2A
Experimental A2A v1.0.1: Agent Card discovery, JSON-RPC SendMessage and the x402 extension at /a2a.
protocols.a2a.enabled is true. Enabling it requires at least one resource with expose: [a2a].| Binding | JSON-RPC 2.0 over HTTP(S) |
| Method | SendMessage (not the legacy message/send) |
| Version | A2A-Version: 1.0 header, or an A2A-Version query parameter when the header is absent; a patch suffix is accepted and ignored |
| Agent Card | GET /.well-known/agent-card.json |
| Default mount | /a2a |
| Task model | synchronous; terminal, except an unpaid x402 extension task |
| Streaming, task lookup, push notifications | unsupported; only unpaid x402 extension tasks are stored, until they expire |
Agent Card
/.well-known/agent-card.jsonpublicResources exposed with expose: [a2a] become skills: skill id = resource id; a paid skill is tagged paid and names its price. A2A v1.0 skills have no input-schema field, so each skill carries an illustrative JSON call example with its required input instead. The agent description explains the call shape, points to <publicBaseUrl>/api/resources for the schemas (see GET /api/resources), and names input._payment when any skill is paid. When x402 is a skill's selected rail, the card advertises the x402 extension as optional, with params: { "x402Version": 2 }.
The card has a content-based ETag and Cache-Control: public, max-age=300. A matching If-None-Match gets 304 with no body.
SendMessage
/a2apayment{
"jsonrpc": "2.0",
"id": "1",
"method": "SendMessage",
"params": {
"message": {
"role": "ROLE_USER",
"messageId": "msg-1",
"parts": [
{
"data": { "resource": "market_report", "input": { "_payment": "<base64 x402 proof>" } },
"mediaType": "application/json"
}
]
}
}
}One message with a non-empty messageId and exactly one data part naming the resource and its input. mediaType is optional; when present it must be application/json. A new purchase refuses text, file, bytes and URL parts, multi-part messages, roles other than ROLE_USER, and task or context continuation.
_payment is this gateway's carrier, not part of A2A or of either rail. MPP defines no A2A binding, so _payment is the only way to pay an MPP skill.
x402 extension
A client that sends A2A-Extensions: https://github.com/google-a2a/a2a-x402/v0.1 can pay an x402 skill without _payment. The gateway uses the x402 v2 documents of the x402 A2A transport with the A2A 1.0 forms: TASK_STATE_INPUT_REQUIRED, ROLE_AGENT, parts without kind, and the A2A-Extensions header.
- Payment required
A call without a proof returns
TASK_STATE_INPUT_REQUIRED. The status message carriesx402.payment.status: payment-requiredand the v2PaymentRequiredinx402.payment.required. - Pay
The client sends a message with that
taskId,x402.payment.status: payment-submittedand thePaymentPayloadinx402.payment.payloadmessage metadata. The gateway validates the stored purchase, verifies the proof, reserves its replay key, calls the backend and settles according to the configured payment flow. - Result
Success is
TASK_STATE_COMPLETEDwithpayment-completedand a settlement entry inx402.payment.receipts. A payment failure isTASK_STATE_FAILEDwithpayment-failed, anx402.payment.errorcode and a failed receipt. A backend failure after settlement fails the task but reportspayment-completedwith the settlement entry. Sendingpayment-rejectedends the task without paying.
Since 2.6.0, unpaid tasks survive restarts in the receipt database until requirement expiry, capped at one hour or ten minutes without an expiry. At 256 pending tasks, new purchases get GATEWAY_BUSY; waiting tasks are not evicted. After validating a payment message, the adapter atomically takes its task before execution, so competing messages find no task.
AP2 mandates remain in memory. After restart, payment for a resource requiring a mandate must include _authorization again; otherwise it returns AUTHORIZATION_REQUIRED after taking the task. An instance with a separate receipt database returns TaskNotFoundError, requiring a new purchase.
A malformed payment message, a contextId other than the task's, a role other than ROLE_USER or a malformed _authorization gets -32602 and leaves the task pending. An x402 v1 payload fails the task with invalid_x402_version. If the first message carried no AP2 mandate, the payment message may add one as {"data": {"input": {"_authorization": {…}}}}; it is verified against the stored purchase. The extension covers x402 only; an MPP skill still returns a terminal task.
Results
| Outcome | Task state | Artifact data |
|---|---|---|
| delivered | TASK_STATE_COMPLETED | the merchant response (non-objects wrapped as { value }); delivery summary in agent-commerce/delivery metadata |
| payment required, without the x402 extension | TASK_STATE_FAILED | the payment envelope |
| domain failure | TASK_STATE_FAILED | the error envelope |
Without the extension, payment required is terminal and the buyer sends a new message carrying the proof. Every failed task has a ROLE_AGENT status message whose text is the envelope's message; after an x402 extension payment, the text summarizes the payment outcome instead.
JSON-RPC errors
Reserved for requests A2A cannot serve — never for commerce outcomes. Each A2A-specific error carries a google.rpc.ErrorInfo in error.data, with a reason such as VERSION_NOT_SUPPORTED and the domain a2a-protocol.org.
| Code | Meaning |
|---|---|
-32700 | bad JSON |
-32600 | bad request object; a request without an id is refused without running |
-32601 | unknown method |
-32602 | bad params or envelope, a missing messageId or a non-user role |
-32001 | TaskNotFoundError: an unknown, expired or completed taskId, and GetTask or CancelTask for any id |
-32003 | the four push-notification configuration methods |
-32004 | other unsupported methods, multi-part messages, a contextId or non-empty referenceTaskIds |
-32005 | a text, file, bytes or URL part, or a non-JSON mediaType |
-32009 | unsupported version; a missing header and parameter, or an empty header, counts as 0.3 (sent as HTTP 200) |
Not implemented: SendStreamingMessage, ListTasks, SubscribeToTask, GetExtendedAgentCard, task lookup and cancellation, push notifications, the REST and gRPC bindings, SSE and A2A authentication schemes. A body over 256 KiB gets 413, and more than 20 requests at once on the mount get 503 GATEWAY_BUSY. The adapter's descriptor.unsupported carries the list at runtime.