JSON-RPC posts every operation to one URL, names it in
method, carries the request message inparams, and returns the response message or an error in a 200 reply.
The planner asks code-reviewer a question before it sends any diff: "What languages can you review?". The card lists http://localhost:41242/a2a/jsonrpc with the binding JSONRPC, so the planner sends one POST to that URL. The reviewer answers with a message and no task, and the record is 02-message-reply.http.
When you finish this section, you can write any A2A call as a JSON-RPC request and read its reply, its stream frames, and its errors.
The envelope
Protocol binding: the form the eleven operations take on one transport, with its names, routes, envelopes, headers, and error shapes. Three are standard: JSON-RPC {{spec §9}}, gRPC {{spec §10}}, and HTTP+JSON {{spec §11}}, and a card names each one in protocolBinding {{proto AgentInterface}}. The Core Concepts guide still says that every request and response uses JSON-RPC 2.0 as its payload format {{docs key-concepts}}. That describes one binding of three (conflict D27).
A JSON-RPC request is one JSON object with four members {{spec §9.3}}. jsonrpc is always 2.0, the client chooses id, method holds the operation name, and params holds the request message. The method names are in PascalCase, like the gRPC names {{spec §9.1}}, so method holds SendMessage or GetTask, the rpc names of A2AService {{proto A2AService}}. The template in §9.3 still shows the 0.3 style, "method": "category/action" {{spec §9.3}}, so copy the names from §9.1 or from operations by binding (conflict D14). The kit answers the old name message/send with -32601, as exchange 3 of 11-errors.http shows.
The reply repeats the request's id and holds the response message in result {{spec §9.4.1}}. For SendMessage that message is a SendMessageResponse with one task or one message {{proto SendMessageResponse}}:
POST /a2a/jsonrpc HTTP/1.1
Content-Type: application/json
…
{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"messageId": "d95bafc8-f2a4-427b-9cf4-bb99f4bea973",
"role": "ROLE_USER",
…
HTTP/1.1 200 OK
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"message": {
"messageId": "3b4b1206-4b6f-467f-b735-2d7e4f7f4bcd",
"contextId": "dc423c62-d5f4-48e5-82c0-4963b9bc4334",
"role": "ROLE_AGENT",
…Original source
title: a question and its direct answer over JSON-RPC
source: capture/out/02-message-reply.http
lang: http
note: The Host and A2A-Version headers and the parts of both messages are cut. [Send message](#s-send-message) explains when the result is a message and when it is a task.
---
POST /a2a/jsonrpc HTTP/1.1
Content-Type: application/json
…
{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"messageId": "d95bafc8-f2a4-427b-9cf4-bb99f4bea973",
"role": "ROLE_USER",
…
HTTP/1.1 200 OK
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"message": {
"messageId": "3b4b1206-4b6f-467f-b735-2d7e4f7f4bcd",
"contextId": "dc423c62-d5f4-48e5-82c0-4963b9bc4334",
"role": "ROLE_AGENT",
…One URL, one content type
Every call is a POST to the URL of the chosen interface, because the binding is JSON-RPC 2.0 over HTTP or HTTPS {{spec §9.1}}. The path carries no operation name, and the kit serves all eleven operations at /a2a/jsonrpc. Both the request and the reply use application/json {{spec §9.1}}, where HTTP+JSON uses its own type, as HTTP+JSON explains.
The headers beside the body are the two service parameters, A2A-Version and A2A-Extensions, which must travel as standard HTTP request headers in this binding {{spec §9.2}}. A2A-Version and A2A-Extensions gives their rules and what each SDK does without them.
Streams as SSE frames
SendStreamingMessage and SubscribeToTask answer with Content-Type: text/event-stream instead of one JSON body {{spec §9.4.2}}. Each data: line is a complete JSON-RPC response: the same id as the request, and one StreamResponse in result {{spec §9.4.2}} {{proto StreamResponse}}. So one parser reads both the single reply and every frame.
HTTP/1.1 200 OK
Content-Type: text/event-stream
data: {"jsonrpc": "2.0", "id": 1, "result": {"task": {"id": "728ba084-97eb-422b-b94b-b0fe9153ce2c", …
data: {"jsonrpc": "2.0", "id": 1, "result": {"statusUpdate": {"taskId": "728ba084-97eb-422b-b94b-b0fe9153ce2c", …Original source
title: the first two frames of a stream, each a full JSON-RPC response
source: capture/out/05-streaming.http
lang: sse
note: The request and the inside of each frame are cut. The file holds all nine frames, and 05-streaming.events.json prints them in full.
---
HTTP/1.1 200 OK
Content-Type: text/event-stream
data: {"jsonrpc": "2.0", "id": 1, "result": {"task": {"id": "728ba084-97eb-422b-b94b-b0fe9153ce2c", …
data: {"jsonrpc": "2.0", "id": 1, "result": {"statusUpdate": {"taskId": "728ba084-97eb-422b-b94b-b0fe9153ce2c", …An HTTP+JSON frame carries the StreamResponse alone, and gRPC returns a server-streaming rpc, as HTTP+JSON and gRPC show. In every binding the frames carry the same four payloads and end for the same reasons, which streaming and subscribing covers.
Errors in the same envelope
A failed call is still a JSON-RPC response: an error member with code, message, and data replaces result {{spec §9.5}}. The specification sets no HTTP status for that case, and the kit and the reference SDK send 200 {{sdk src/a2a/server/routes/jsonrpc_dispatcher.py}}. The A2A errors take the codes -32001 to -32009, and the detail that names each one is in data. Errors reads them in every binding.
Original source
- Post every operation to the one JSON-RPC URL on the card, with the PascalCase name in `method`.
- Send `Content-Type: application/json`, and read the response message from `result`.
- Parse each SSE `data:` line as a complete JSON-RPC response with the request's `id`.
- Check every reply and frame for an `error` member before you read `result`.Sources: spec §9, §9.1, §9.2, §9.3, §9.4.1, §9.4.2, §9.5, §10, §11 (research/sources/specification.md); proto AgentInterface, A2AService, SendMessageResponse, StreamResponse (research/sources/a2a.proto); docs key-concepts (research/sources/docs.md); sdk src/a2a/server/routes/jsonrpc_dispatcher.py (a2a-python 1.2.2); capture/out/02-message-reply.http, 05-streaming.http, 05-streaming.events.json, 11-errors.http; capture/a2a_ref.py