HTTP+JSON
HTTP+JSON names each operation with a verb and a path from the proto's google.api.http annotations, and sends the request and response messages as plain JSON bodies.
The planner repeats its first test run over the second interface of test-runner, http://localhost:41241/a2a/rest with the binding HTTP+JSON. The same agent and task store answer, in 16-rest-binding.http, and only the envelope differs from 03-blocking-task.http.
When you finish this section, you can map any operation to its route, set its content type, and send a tenant when the card asks.
Routes from the proto
Every rpc of A2AService carries an option (google.api.http) with a verb and a path template, and §11.3 lists the same routes. An operation with a request body is a POST with body: "*": /message:send, /message:stream, /tasks/{id}:cancel, and /tasks/{task_id}/pushNotificationConfigs. Reads are GET, and the configuration delete is DELETE. Operations by binding gives all eleven routes with their messages.
For subscribe the proto says get: "/tasks/{id=*}:subscribe", and §5.3 and §11.3.2 say POST /tasks/{id}:subscribe (conflict D1). The reference SDK's server accepts both verbs, and its client sends POST. The Java and .NET servers accept only POST. So accept both on a server, and send POST from a client.
A2A-Version: 1.0
Content-Type: application/a2a+json
{
"message": {
"messageId": "48f165d5-7b00-47f4-b81e-f86f5c8cc1ab",
"role": "ROLE_USER",
…
Content-Type: application/a2a+json
{
"task": {
"id": "bbfb9ea5-50ea-4441-b991-2a8ba0d6f3f3",
"contextId": "41c0d462-fed8-4f6b-8569-6c9afe9497c3",
…Query parameters and content types
GET and DELETE carry no body, so their request fields travel as path parameters or query parameters, in camelCase. GetTask puts the task id in the path and historyLength in the query. ListTasks takes contextId, status, pageSize, pageToken, historyLength, statusTimestampAfter, and includeArtifacts. A nested object cannot go in a query string, so that request must be a POST. Figure 5.1 sets both operations beside their JSON-RPC form.
Requests and responses should use application/a2a+json, and the kit sends it on every body. The reference SDK sends its errors as application/json, and the Java and Go servers send every body that way. So send application/a2a+json, and accept both types when you read.
Streams and errors
A stream is the same text/event-stream response as in JSON-RPC. Each data: line holds the StreamResponse alone, with no envelope. No capture records a stream in this binding, so streaming and subscribing shows the JSON-RPC frames.
An error uses the HTTP status code and the JSON form of google.rpc.Status in an error object. Exchanges 3 and 4 of the record show two, and Errors reads that body.
The tenant field
Tenant: an optional opaque string on an interface that routes requests to one agent, or to one tenant when several agents share one endpoint. The same tenant field sits on all ten request messages, from SendMessageRequest to GetExtendedAgentCardRequest. A client copies the card's value into every request, and must leave the field out when the entry sets no tenant. The protocol gives the value no format, and no kit card sets one.
Here the value can also travel in the path. Every rpc has a second google.api.http form with a /{tenant} prefix, such as post: "/{tenant}/message:send". Only the proto lists these routes, and §5.3, §11.3, and §11.5 show the plain ones. In JSON-RPC the value is params.tenant, and in gRPC it is the message field. The migration guide describes this as tenant scoping built into gRPC requests, which understates it.
The project's guide names three routing keys for several agents on one host: a URL prefix, the credential, and the tenant field. A deployment can use more than one of them.
The reference SDK's client wraps the transport in a TenantTransportDecorator when the chosen interface sets a tenant. Its HTTP+JSON transport puts the value in the path prefix. Its server mounts every route again under /{tenant}, copies the value into the call context, and never compares it with the card. The agent reads RequestContext.tenant and decides what it means.
A2A protocol 1.0.1 · 3303592 · 2026-05-28 · Edition 2026.10