Learn

Moving from A2A 0.3 to 1.0

Every breaking change between A2A 0.3 and 1.0, a before and after example, a step-by-step migration checklist, and how one endpoint can serve both versions.

A2A 1.0 keeps the concepts of 0.3 (agents, cards, tasks, messages, artifacts) and changes almost everything on the wire. Method names move to PascalCase, enum values move to ProtoJSON names such as TASK_STATE_COMPLETED and ROLE_USER, the kind discriminator disappears from parts and events, the card’s endpoint moves into supportedInterfaces, and errors carry typed google.rpc.ErrorInfo details. Version 1.0.0 was released on 12 March 2026 and the v1.0.1 patch on 28 May 2026. The protocol version string is 1.0.

Clients now declare their version with the A2A-Version header, and a request without it must be read as 0.3. That rule lets one endpoint serve both versions during a migration. This guide lists every breaking change from the specification’s migration appendix, the What’s New document and the v1.0.0 release notes, then gives a checklist and shows how a live agent answers both versions.

Method names

v0.3 JSON-RPC method v1.0 method
message/send SendMessage
message/stream SendStreamingMessage
tasks/get GetTask
No JSON-RPC method (0.3 listed tasks/list for gRPC and REST only) ListTasks, in every binding, with cursor pagination
tasks/cancel CancelTask
tasks/resubscribe SubscribeToTask
tasks/pushNotificationConfig/set CreateTaskPushNotificationConfig
tasks/pushNotificationConfig/get GetTaskPushNotificationConfig
tasks/pushNotificationConfig/list ListTaskPushNotificationConfigs (now plural)
tasks/pushNotificationConfig/delete DeleteTaskPushNotificationConfig
agent/getAuthenticatedExtendedCard GetExtendedAgentCard

Appendix A also renames the request and response types, which matters if your SDK exposes them: MessageSendParams became SendMessageRequest, SendMessageSuccessResponse became SendMessageResponse, SendStreamingMessageSuccessResponse became StreamResponse, SetTaskPushNotificationConfigRequest became CreateTaskPushNotificationConfigRequest, ListTaskPushNotificationConfigSuccessResponse became ListTaskPushNotificationConfigsResponse, and GetAuthenticatedExtendedCardRequest became GetExtendedAgentCardRequest.

Roles and states

v0.3 v1.0
user, agent ROLE_USER, ROLE_AGENT
submitted, working TASK_STATE_SUBMITTED, TASK_STATE_WORKING
input-required, auth-required TASK_STATE_INPUT_REQUIRED, TASK_STATE_AUTH_REQUIRED
completed, failed, canceled, rejected TASK_STATE_COMPLETED, TASK_STATE_FAILED, TASK_STATE_CANCELED, TASK_STATE_REJECTED
unknown No listed mapping; the v1.0 zero value is TASK_STATE_UNSPECIFIED

gRPC users have one more rename. The v0.3 proto spelled TASK_STATE_CANCELLED with two Ls, and 1.0 standardized on the American spelling, TASK_STATE_CANCELED.

Parts, messages and events

Version 1.0 has one Part type instead of three, and the JSON member that is present says what kind of part it is.

v0.3 v1.0
{"kind": "text", "text": "..."} {"text": "..."}
{"kind": "data", "data": {...}} {"data": {...}, "mediaType": "application/json"}
{"kind": "file", "file": {"name", "mimeType", "uri"}} {"url": "...", "filename": "...", "mediaType": "..."}
{"kind": "file", "file": {"name", "mimeType", "bytes"}} {"raw": "...", "filename": "...", "mediaType": "..."}
mimeType mediaType, available on every part

The v0.3 JSON schema named the file fields uri and bytes; the migration appendix uses the gRPC names fileWithUri and fileWithBytes for the same fields.

Stream events changed the same way. {"kind": "status-update", ...} became {"statusUpdate": {...}} and {"kind": "artifact-update", ...} became {"artifactUpdate": {...}}. The final flag on status updates is gone; the stream closing marks the end. Messages and artifacts gained an extensions list of extension URIs. Timestamps are ISO 8601 in UTC, with millisecond precision where available.

Agent Card

v0.3 v1.0
Top-level url and preferredTransport An entry in supportedInterfaces, each with url, protocolBinding and protocolVersion
additionalInterfaces More entries in supportedInterfaces, in preference order
Top-level protocolVersion, such as "0.3.0" protocolVersion per interface, as Major.Minor, such as "1.0"
supportsAuthenticatedExtendedCard capabilities.extendedAgentCard
Implicit and password OAuth flows Deprecated in the proto; use authorization code with PKCE, or the new device code flow
No tenant Optional tenant per interface, sent by the client in every request

The extended card flag moved twice during 1.0 development. It was first renamed to supportsExtendedAgentCard, then moved into capabilities. Appendix A shows that interim name as the legacy one, but a real 0.3 card uses supportsAuthenticatedExtendedCard. A client in transition can accept either legacy name.

The discovery path did not change. Version 0.3.0 had already moved the card from /.well-known/agent.json, the v0.2.x path, to /.well-known/agent-card.json. See Agent Cards explained for every v1.0 field.

Requests and bindings

  • SendMessage configuration: pushNotificationConfig became taskPushNotificationConfig, and blocking gave way to returnImmediately. Blocking is the default in 1.0, and a blocking call returns at interrupted states too.
  • Push configs are flat. The 0.3 wrapper {taskId, pushNotificationConfig: {...}} became one object with id, taskId, url, token and authentication, and authentication.schemes (a list) became scheme (one value).
  • IDs are plain values. gRPC and HTTP requests that took resource names such as tasks/{id} now take separate ID fields.
  • HTTP+JSON paths lost their /v1 prefix: POST /v1/message:send is now POST /message:send. Since v1.0.1 the preferred content type is application/a2a+json.
  • gRPC: the proto package moved from a2a.v1 to lf.a2a.v1, and the SendMessageRequest field request is now message, matching the other bindings.

Headers and errors

A2A-Version is new in 1.0. Clients must send it on every request, except 0.3 clients, which had no such header. Servers must read an empty value as 0.3, must process a request with the semantics of the version it names, and must return VersionNotSupportedError for one they don’t serve. Patch numbers don’t count in negotiation. The extension activation header was renamed too: 0.3’s guide used X-A2A-Extensions, and 1.0 uses A2A-Extensions.

Errors keep their JSON-RPC codes but change shape. In 0.3, error.data was free-form. In 1.0 it is an array of typed objects, and A2A errors include a google.rpc.ErrorInfo with a reason such as TASK_NOT_FOUND and the domain a2a-protocol.org. HTTP+JSON errors moved from RFC 9457 problem details (application/problem+json) to the google.rpc.Status JSON shape.

Code v0.3 name v1.0 name
-32001 to -32006 Unchanged Unchanged
-32007 AuthenticatedExtendedCardNotConfiguredError ExtendedAgentCardNotConfiguredError
-32008 None ExtensionSupportRequiredError
-32009 None VersionNotSupportedError

Before and after

Illustrative: the same interrupted task in both versions. First v0.3:

{
  "kind": "task",
  "id": "task-51",
  "contextId": "ctx-9",
  "status": {
    "state": "input-required",
    "timestamp": "2026-09-26T15:00:00Z",
    "message": {
      "kind": "message",
      "role": "agent",
      "messageId": "msg-2",
      "parts": [{ "kind": "text", "text": "Which pickup date should I quote?" }]
    }
  },
  "artifacts": [
    {
      "artifactId": "draft-1",
      "parts": [
        { "kind": "data", "data": { "lane": "Toronto-Chicago" } },
        { "kind": "file", "file": { "name": "rate-sheet.pdf", "mimeType": "application/pdf", "uri": "https://files.example.com/rate-sheet.pdf" } }
      ]
    }
  ]
}

Then v1.0:

{
  "id": "task-51",
  "contextId": "ctx-9",
  "status": {
    "state": "TASK_STATE_INPUT_REQUIRED",
    "timestamp": "2026-09-26T15:00:00.000Z",
    "message": {
      "role": "ROLE_AGENT",
      "messageId": "msg-2",
      "parts": [{ "text": "Which pickup date should I quote?" }]
    }
  },
  "artifacts": [
    {
      "artifactId": "draft-1",
      "parts": [
        { "data": { "lane": "Toronto-Chicago" }, "mediaType": "application/json" },
        { "url": "https://files.example.com/rate-sheet.pdf", "filename": "rate-sheet.pdf", "mediaType": "application/pdf" }
      ]
    }
  ]
}

Migration checklist

For clients:

  1. Send A2A-Version: 1.0 on every request.
  2. Rename every method call, using the table above.
  3. Compare states and roles against the TASK_STATE_* and ROLE_* values. Treat any value you don’t recognize as unknown instead of failing.
  4. Read parts by member presence (text, data, url, raw) and build them without kind. Rename mimeType to mediaType.
  5. Read stream events by member name, and treat the stream closing as the end.
  6. Pick an endpoint from supportedInterfaces: the first entry whose binding and version you support. Send its tenant if it has one. Read capabilities.extendedAgentCard.
  7. Read error.data as an array and branch on ErrorInfo.reason. Handle -32008 and -32009.
  8. Replace blocking with returnImmediately, and send push configs in the flat shape.
  9. Send extension URIs in A2A-Extensions.
  10. On HTTP+JSON, drop /v1 from paths. On gRPC, regenerate from the 1.0 proto.

For servers:

  1. Publish supportedInterfaces with one entry per binding and version, and remove the old top-level fields.
  2. Move the extended card flag into capabilities.
  3. Re-sign the card if you sign it. The signature covers the canonical card, so any edit breaks the old one.
  4. Validate A2A-Version: read empty as 0.3, reject unsupported versions with -32009.
  5. Emit v1.0 shapes for v1.0 requests, with no kind. The appendix recommends emitting only current forms.
  6. Return errors with ErrorInfo details and the new names.
  7. If you add ListTasks, scope results to what the caller may see, as section 13.1 requires.

What’s New suggests three phases. First, a compatibility layer that parses both the old and new forms. Second, dual support: emit 1.0 everywhere, keep 0.3 readers, and add A2A-Version handling. Third, 1.0 only: remove the legacy parsing once your callers have moved. Its testing advice covers data in both formats, card signature checks, pagination edge cases such as empty and single-page results, the new error types, and required-extension checks. Section 3.6.3 adds a warning for clients: if you need 1.0 features, request 1.0 explicitly and don’t fall back to 0.3 automatically, or you lose those features without noticing.

Serving both versions at once

Emissar’s public agent at https://emissar.ai/a2a/v1 answers 0.3 and 1.0 on one JSON-RPC endpoint. Its behavior, as of 2026-09-26:

Request What the agent does
A 1.0 method name, such as SendMessage Answers in 1.0 shapes
A 0.3 method name: message/send, tasks/get or tasks/cancel Answers in 0.3 shapes: kind fields, lowercase states and roles
A2A-Version of 1.0 or 0.3, with or without a patch number Accepted; the method name still decides the shapes
No A2A-Version Accepted
Any other A2A-Version -32009, before the method runs
0.3 or 1.0 names for streaming, push configs or the extended card The same refusals in both: -32004 or -32003

Both versions read and write one task store, so a task created over 1.0 can be read with tasks/get. Dispatching on the method name works because the two versions share no method names. The header check stops versions the agent doesn’t serve.

A real 0.3 call with no A2A-Version header, on 2026-09-26:

curl -s https://emissar.ai/a2a/v1 \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc": "2.0", "id": 8, "method": "message/send",
       "params": {"message": {"kind": "message", "role": "user", "messageId": "smoke-test-docs-v03-1",
                              "parts": [{"kind": "text", "text": "What does Emissar do?"}]}}}'
{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "kind": "task",
    "id": "02f322e1-48e5-4572-b044-c4c20982c1c3",
    "contextId": "bbc62fd0-5b0c-4c55-8eaa-535f67dce4d2",
    "status": {
      "state": "completed",
      "timestamp": "2026-09-26T20:21:18.079Z"
    },
    "artifacts": [
      {
        "artifactId": "adbe8179-58e6-4e3f-aa40-926b9b946e03",
        "name": "Answer",
        "parts": [
          {
            "kind": "text",
            "text": "Emissar is building the network where AI agents from different companies find each other, prove who they represent, and settle requests directly, instead of talking over phone lines and chat widgets built for people. It builds on the open A2A protocol and adds what A2A leaves open: finding an agent from a phone number or name, verifying agents, scoped authorization, signed records, payments, and handoff to people."
          }
        ]
      }
    ],
    "history": [
      {
        "kind": "message",
        "role": "user",
        "parts": [
          {
            "kind": "text",
            "text": "What does Emissar do?"
          }
        ],
        "messageId": "smoke-test-docs-v03-1",
        "taskId": "02f322e1-48e5-4572-b044-c4c20982c1c3",
        "contextId": "bbc62fd0-5b0c-4c55-8eaa-535f67dce4d2"
      }
    ],
    "metadata": {
      "skill": "about-emissar"
    }
  }
}

A SendMessage with A2A-Version: 0.5 returned -32009 with the reason VERSION_NOT_SUPPORTED. The bindings guide shows the full response.

Discovery is the harder part of serving both. The agent publishes its 1.0 card at /.well-known/agent-card.json and a 0.3-shaped card, with url and preferredTransport, at /.well-known/agent.json. A 0.3.0 client looks for agent-card.json, so it receives the 1.0 card, which has no top-level url. There are two ways to close that gap. One is to add the 0.3 top-level fields to the 1.0 card; section 5.7 says implementations should ignore fields they don’t recognize, so 1.0 clients should skip them. The other is to configure 0.3 clients with the endpoint directly. Test either choice against the SDKs your callers use.

Questions

Did the Agent Card location change between 0.3 and 1.0?
No. Version 0.3.0 moved the card from /.well-known/agent.json to /.well-known/agent-card.json, and 1.0 kept that path. Only v0.2.x agents used agent.json.
What happens if a 1.0 client forgets the A2A-Version header?
The server must treat the request as 0.3. A strict 0.3-only reading would not recognize a method such as SendMessage. Some servers are lenient and decide by method name, but a client should not rely on that: send A2A-Version: 1.0 on every request.
Do I have to drop 0.3 support once I move to 1.0?
No. An agent can expose several interfaces with different protocol versions, at the same URL or at different ones, and must process each request with the semantics of the version it names.

Sources

  1. A2A Protocol Specification, Appendix A: Migration and legacy compatibility (plus sections 3.6 and 5) (accessed )
  2. What's new in A2A v1.0 (A2A documentation) (accessed )
  3. A2A v1.0.0 release notes (accessed )
  4. A2A v1.0.1 release notes (accessed )
  5. A2A v0.3.0 release notes (well-known URI changed to agent-card.json) (accessed )
  6. A2A Protocol Specification v0.3.0 (accessed )
  7. A2A v0.3.0 JSON types (types.ts) (accessed )
  8. A2A v0.3.0 gRPC definition (a2a.proto) (accessed )
  9. A2A v0.3.0 extensions guide (X-A2A-Extensions header) (accessed )
  10. A2A protocol definition (a2a.proto), current (accessed )
  11. Emissar Agent Card (live example agent) (accessed )