>_ DOCS / REFERENCE
A2A
PROTOCOL.
P402 implements the Google A2A (Agent-to-Agent) protocol over JSON-RPC 2.0. This page is the complete reference: task lifecycle, all four RPC methods, SSE streaming, and the x402 payment extension.
What is A2A?
A2A is a standardized communication protocol that lets AI agents talk to each other using structured tasks over JSON-RPC 2.0. Instead of each agent defining its own API format, A2A provides a shared vocabulary that any conforming agent can understand.
P402 extends A2A with the x402 payment extension, which adds machine-native payment negotiation to any A2A task. An agent can request USDC payment, receive a signed authorization from the caller, and confirm settlement — all within the same task context.
Task Lifecycle
Every unit of work in A2A is a Task. A task moves through a fixed state machine. Understanding these states is essential for building agents that handle edge cases correctly.
| State | Meaning | Terminal? |
|---|---|---|
| pending | Task received, queued for processing. | No |
| processing | Agent is actively working on the task. | No |
| completed | Task finished successfully. Artifacts are available. | Yes |
| failed | Task encountered an unrecoverable error. Check status.message. | Yes |
| cancelled | Task was explicitly cancelled via tasks/cancel. | Yes |
Task Object
All methods that return a task return this structure.
{
"id": "task_01J...",
"contextId": "ctx_...", // Groups related tasks in a conversation
"status": {
"state": "completed", // pending | processing | completed | failed | cancelled
"message": { // Set when state = failed; explains the error
"role": "agent",
"parts": [{ "type": "text", "text": "Error: upstream provider timeout" }]
},
"timestamp": "2026-04-16T12:00:00.000Z"
},
"artifacts": [ // Output produced by the task
{
"name": "completion",
"parts": [{ "type": "text", "text": "The answer is 42." }]
}
],
"metadata": {
"cost_usd": 0.0003,
"latency_ms": 1240,
"provider": "deepseek",
"model": "deepseek-v3"
}
}JSON-RPC Methods
All requests are POST /api/a2a with the standard JSON-RPC 2.0 envelope. The method field selects the operation. All methods require a Authorization: Bearer <api_key> header.
tasks/sendSubmits a new task. The router processes the request synchronously and returns the completed (or failed) task. Use this for short tasks where you can wait for the result.
{
"jsonrpc": "2.0",
"method": "tasks/send",
"id": 1,
"params": {
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "Summarize this in one sentence: [...]" }]
},
"configuration": {
"mode": "cost", // cost | speed | quality | balanced
"session_id": "ses_..." // optional: attach to a budget-capped session
}
}
}{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "task_01J...",
"status": { "state": "completed", "timestamp": "2026-04-16T12:00:01.240Z" },
"artifacts": [{
"name": "completion",
"parts": [{ "type": "text", "text": "The document outlines three key themes." }]
}],
"metadata": { "cost_usd": 0.0003, "latency_ms": 1240, "provider": "deepseek" }
}
}tasks/sendSubscribeSubmits a task and immediately opens an SSE stream for live updates. The connection stays open until the task reaches a terminal state. Use this for long-running tasks or when you want to stream tokens to a UI.
{
"jsonrpc": "2.0",
"method": "tasks/sendSubscribe",
"id": 1,
"params": {
"message": {
"role": "user",
"parts": [{ "type": "text", "text": "Write a 500-word report on..." }]
},
"configuration": { "mode": "quality" }
}
}data: {"id":"task_01J...","status":{"state":"processing","timestamp":"..."}}
data: {"id":"task_01J...","status":{"state":"processing"},"delta":{"type":"text","text":"The "}}
data: {"id":"task_01J...","status":{"state":"processing"},"delta":{"type":"text","text":"report "}}
data: {"id":"task_01J...","status":{"state":"completed","timestamp":"..."},"artifacts":[...]}const response = await fetch('https://p402.io/api/a2a', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.P402_API_KEY}`,
'Content-Type': 'application/json',
'Accept': 'text/event-stream',
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'tasks/sendSubscribe',
id: 1,
params: {
message: { role: 'user', parts: [{ type: 'text', text: 'Hello' }] },
configuration: { mode: 'speed' },
},
}),
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let output = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
const lines = decoder.decode(value).split('\n');
for (const line of lines) {
if (!line.startsWith('data: ')) continue;
const event = JSON.parse(line.slice(6));
if (event.delta?.type === 'text') output += event.delta.text;
if (event.status?.state === 'completed') break;
}
}
console.log(output);tasks/getRetrieves a task by ID. Use this to poll for completion when you submitted with tasks/send and want to check status asynchronously.
{
"jsonrpc": "2.0",
"method": "tasks/get",
"id": 2,
"params": { "id": "task_01J..." }
}tasks/cancelCancels an in-progress task. Only tasks in pending or processing state can be cancelled. Cancelling a terminal task returns an error.
{
"jsonrpc": "2.0",
"method": "tasks/cancel",
"id": 3,
"params": { "id": "task_01J..." }
}
// Response: task with state = "cancelled"
{
"jsonrpc": "2.0",
"id": 3,
"result": { "id": "task_01J...", "status": { "state": "cancelled", "timestamp": "..." } }
}Agent Discovery
Before calling an agent, you can discover its capabilities by fetching its AgentCard — a machine-readable JSON manifest served at the standard well-known URL.
curl https://p402.io/.well-known/agent.json{
"protocolVersion": "1.0",
"name": "P402 Payment Router",
"description": "AI orchestration router with x402 payment settlement.",
"url": "https://p402.io",
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"skills": [
{
"id": "ai-completion",
"name": "Chat Completion",
"description": "Route a chat completion to the optimal provider",
"tags": ["ai", "llm", "routing"]
}
],
"extensions": [
{
"uri": "tag:x402.org,2025:x402-payment",
"description": "Accepts x402 EIP-3009 USDC payments on Base"
}
],
"endpoints": {
"a2a": {
"jsonrpc": "https://p402.io/api/a2a",
"stream": "https://p402.io/api/a2a/stream"
}
}
}x402 Payment Extension
When an agent declares the x402-payment extension in its AgentCard, it can require payment before completing a task. The payment negotiation happens entirely within the A2A task context — no separate payment API call.
The flow has three steps: the agent sends a payment request, the caller submits a signed EIP-3009 authorization, and the agent confirms settlement before delivering the result.
Agent sends payment-required
The agent responds to the initial task with a payment request embedded in the result. The task state remains processing until payment is received.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "task_01J...",
"status": { "state": "processing" },
"extension": {
"uri": "tag:x402.org,2025:x402-payment",
"content": {
"type": "payment-required",
"data": {
"payment_id": "pay_abc123",
"amount_usd": 0.05,
"schemes": [{
"scheme": "exact",
"asset": "USDC",
"network": "eip155:8453",
"amount": "50000", // 0.05 USDC in atomic units (6 decimals)
"payTo": "0xFa772434DCe6ED78831EbC9eeAcbDF42E2A031a6"
}],
"valid_until": "2026-04-16T12:05:00.000Z"
}
}
}
}
}Caller submits signed payment
The caller signs an EIP-712 TransferWithAuthorization and submits it as a new task message. P402 verifies the signature and nonce before forwarding to the agent.
{
"jsonrpc": "2.0",
"method": "tasks/send",
"id": 2,
"params": {
"id": "task_01J...", // Continue the same task
"message": {
"role": "user",
"parts": [{
"type": "data",
"data": {
"extension": "tag:x402.org,2025:x402-payment",
"type": "payment-submitted",
"payment_id": "pay_abc123",
"scheme": "exact",
"authorization": {
"from": "0xYourWalletAddress",
"to": "0xFa772434DCe6ED78831EbC9eeAcbDF42E2A031a6",
"value": "50000",
"validAfter": "1713261600",
"validBefore": "1713265200",
"nonce": "0xabc123..."
},
"signature": "0x..." // EIP-712 signature over the authorization
}
}]
}
}
}Agent confirms and delivers result
After P402 settles the on-chain transfer, the agent receives confirmation and completes the original task. The response includes a receipt ID for your records.
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"id": "task_01J...",
"status": { "state": "completed", "timestamp": "2026-04-16T12:00:45.000Z" },
"artifacts": [{
"name": "completion",
"parts": [{ "type": "text", "text": "Here is the premium analysis you requested..." }]
}],
"extension": {
"uri": "tag:x402.org,2025:x402-payment",
"content": {
"type": "payment-completed",
"payment_id": "pay_abc123",
"receipt_id": "rec_789xyz",
"tx_hash": "0x..."
}
},
"metadata": { "cost_usd": 0.05, "latency_ms": 3200 }
}
}Error Handling
A2A errors are returned as standard JSON-RPC error objects. The HTTP status is always 200 — errors are embedded in the JSON payload, not in the HTTP status code. This lets orchestrators handle them without HTTP error detection logic.
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32000,
"message": "BILLING_CAP_REACHED",
"data": {
"budget_usd": 10.00,
"spent_usd": 10.00,
"session_id": "ses_..."
}
}
}| Code | Message | Meaning & Action |
|---|---|---|
| -32700 | Parse error | Request body is not valid JSON. Fix the serialization. |
| -32600 | Invalid request | Missing jsonrpc, method, or id. Check the envelope structure. |
| -32601 | Method not found | Unknown method. Valid: tasks/send, tasks/get, tasks/cancel, tasks/sendSubscribe. |
| -32602 | Invalid params | Method params are malformed. Check the required fields. |
| -32000 | BILLING_CAP_REACHED | Session budget exhausted. Create a new session or increase the budget. |
| -32000 | TASK_NOT_FOUND | tasks/get or tasks/cancel called with an unknown task ID. |
| -32000 | TASK_ALREADY_TERMINAL | tasks/cancel called on a completed/failed task. |
| -32000 | PAYMENT_EXPIRED | x402 payment authorization expired before it was submitted. |
| -32000 | PAYMENT_INVALID | x402 signature failed verification. Re-sign and resubmit. |
Billing errors are not HTTP 402
When an agent hits a billing cap, P402 maps it to JSON-RPC error code -32000 with message BILLING_CAP_REACHED. It does NOT return HTTP 402. This prevents orchestrators from misinterpreting it as an x402 payment request rather than a budget error.