Learn A2A - JSON-RPC Methods & HTTP Binding
Series/Learn A2A/Episode 5
Episode 5 of 23

Learn A2A - JSON-RPC Methods & HTTP Binding

Dissecting A2A's protocol layer: the five core JSON-RPC methods from message/send, tasks/get, tasks/cancel, tasks/pushNotificationConfig, to messages/list, complete with HTTP POST binding practice and the SSE endpoint for streaming.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 4 we understood Task and Message as the data model. Now it's time to see how that model is called: the JSON-RPC methods available and how those methods attach to HTTP.

Episode 5 is the most "protocol-heavy" episode so far. You'll memorize the five core methods, then practice them directly with curl against an A2A server. After this episode, you'll be able to trace A2A traffic like reading a book.

JSON-RPC 2.0 Principles in A2A

A2A uses JSON-RPC 2.0 as its invocation language. Every request is an object with three required fields:

JSON-RPC request structure
{
  "jsonrpc": "2.0",
  "id": "call-001",
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [{ "text": "Halo" }]
    }
  }
}
  • jsonrpc — always the value 2.0.
  • id — the request's unique marker; the response returns the same id so the client can match them up.
  • method — the name of the method being called.
  • params — the method's arguments; their shape is specific to each method.

Unlike REST, which has many endpoints per resource, JSON-RPC uses just one endpoint with different methods inside the body. This simplicity is what makes cross-transport binding (HTTP, gRPC, SSE) possible.

Core Method: message/send

The most important method — used to send the first message and create a new task at the same time. If params.taskId is absent, the remote agent creates a new task and returns its id.

Create a task via message/send
curl -s -X POST https://analisis.example.com \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":"1","method":"message/send","params":{"message":{"role":"user","parts":[{"text":"Analisis laporan ini"}]}}}'

The response carries the task object complete with its id and initial status:

message/send response
{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "id": "task-7f3a",
    "status": { "state": "submitted" }
  }
}

From this response the client gets the taskId that will be used in all subsequent methods.

Core Methods: tasks/get and tasks/cancel

tasks/get

To fetch a task's current state — including its message history — the client calls tasks/get with a taskId. This method is essential for the polling pattern when streaming is unavailable.

tasks/get request
{
  "jsonrpc": "2.0",
  "id": "2",
  "method": "tasks/get",
  "params": {
    "taskId": "task-7f3a",
    "historyLength": 10
  }
}

The historyLength parameter limits the number of messages returned. For tasks with long conversations, this prevents the payload from bloating.

tasks/cancel

When a task is no longer relevant — for example, requirements change midway — the client cancels it with tasks/cancel:

tasks/cancel request
{
  "jsonrpc": "2.0",
  "id": "3",
  "method": "tasks/cancel",
  "params": {
    "taskId": "task-7f3a"
  }
}

Cancellation changes the task status to canceled. A good remote agent will stop in-progress work and free its resources.

Core Method: tasks/pushNotificationConfig

This method switches the communication mode from client pulling (polling) to agent pushing (push). The client registers a webhook URL the remote agent will call whenever the task updates:

Registering a push webhook
{
  "jsonrpc": "2.0",
  "id": "4",
  "method": "tasks/pushNotificationConfig",
  "params": {
    "taskId": "task-7f3a",
    "pushNotificationConfig": {
      "url": "https://client.example.com/hooks/a2a"
    }
  }
}

Once registered, the remote agent sends a notification to that URL every time the task status changes — without waiting for the client to ask. The full details of streaming and push, including retry and polling fallback, will be explored in episode 7.

Core Method: messages/list

A task's complete conversation history can be fetched with messages/list. Unlike tasks/get, which carries a status snapshot, this method focuses on all the messages:

messages/list request
{
  "jsonrpc": "2.0",
  "id": "5",
  "method": "messages/list",
  "params": {
    "taskId": "task-7f3a"
  }
}

The response is an ordered array of messages — from the user's first message to the agent's last. This method is useful for rebuilding a conversation's context from scratch, for example when a client joins a task that has been running for a while.

HTTP Binding: POST to a Single Endpoint

All the methods above are carried over POST to a single endpoint — usually the URL listed in the Agent Card. Summary:

Summary of the A2A HTTP binding
POST https://analisis.example.com     -> all JSON-RPC methods
GET  https://analisis.example.com/.well-known/agent-card, /sse

The rules are consistent with the HTTP specification: a Content-Type: application/json header, a JSON-RPC request body, and responses return with status 200 as long as the request is valid — even when the task fails, because the failure is carried in the JSON-RPC body, not the HTTP status code.

Warning

Don't get into the habit of relying on HTTP status codes to judge a task's outcome. A 200 code only means "request received and processed", not "task succeeded". For that, read result.status.state in the response body.

SSE Endpoint for Streaming

If the Agent Card declares streaming: true, the client can open an SSE channel to receive task updates in real time:

Opening an SSE channel
curl -s -N https://analisis.example.com/sse?taskId=task-7f3a
SSE event stream
event: task_update
data: {"state":"working","message":{"role":"agent","parts":[{"text":"Menganalisis..."}]}}
 
event: task_update
data: {"state":"completed","message":{"role":"agent","parts":[{"text":"Selesai!"}]}}

Each event carries a message delta or a status change. Try it directly in your terminal with curl -s -N https://analisis.example.com/sse?taskId=task-7f3a. The client closes the channel after receiving a terminal status (completed, failed, or canceled). The combination of POST for requests and SSE for notifications is what's called the HTTP+SSE binding.

Conclusion

Here's the core takeaway:

  • A2A uses JSON-RPC 2.0 with the jsonrpc, id, method, and params fields.
  • message/send creates a task and sends the first message.
  • tasks/get and tasks/cancel manage tasks: read the state and cancel.
  • tasks/pushNotificationConfig registers a webhook so the agent pushes updates.
  • messages/list fetches the complete conversation history.
  • The HTTP binding uses a single POST endpoint for all methods; SSE is used for streaming deltas.

In episode 6 we move from theory to real practice: Python & TypeScript SDK — installing a2a-sdk for Python and @a2a-js/sdk for TypeScript, writing task handlers with decorators, and running your first A2A agent. See you there!