How A2A works

The A2A (Agent-to-Agent) protocol lets AI agents discover, understand, and invoke each other’s capabilities through a standard Agent Card and JSON-RPC transport.

Runtype A2A Surfaces speak the A2A Protocol v1.0 wire format. Use the v1.0 PascalCase method names, ProtoJSON enum values, and member-presence message parts shown below.

Discovery phase

External agents discover your capabilities by fetching the Agent Card:

$GET https://api.runtype.com/v1/products/{productId}/surfaces/{surfaceId}/a2a/.well-known/agent-card.json

The Agent Card describes your product’s identity, skills, supported interfaces, and authentication schemes. It is publicly accessible so agents can discover the surface before they send an authenticated invocation.

Invocation phase

Once discovered, external agents invoke capabilities by sending JSON-RPC requests to the A2A endpoint:

$POST https://api.runtype.com/v1/products/{productId}/surfaces/{surfaceId}/a2a

The canonical v1.0 methods are:

  • SendMessage — execute a task synchronously.
  • SendStreamingMessage — execute a task with SSE streaming.
  • GetTask — fetch a task by ID.
  • ListTasks — list tasks with cursor pagination.
  • CancelTask — cancel a running task.
  • SubscribeToTask — resume an SSE stream for a task.

Runtype is tolerant of legacy slash-string method aliases from older A2A clients, but new clients should use the v1.0 method names above.

Authentication uses either header:

  • Authorization: Bearer a2a_xxx
  • X-API-Key: a2a_xxx

Orchestrated routing

When an A2A Surface uses Managed orchestration, the external agent does not need to choose a specific skill. Instead:

  1. The external agent sends a natural-language request.
  2. Runtype analyzes the request against the Product’s capabilities.
  3. Runtype routes to the right Flow or Agent.
  4. The A2A task response is returned to the requesting agent.

Use Delegate mode when each capability should be exposed as a distinct skill and the caller should choose the skill itself. Use Managed mode when you want one product-level entry point.

Protocol standards

Runtype’s exposed A2A Surface follows the A2A Protocol v1.0.

Key wire-format details:

  • JSON-RPC 2.0 transport for every method.
  • Message parts use member presence: { "text": "..." }, { "data": { ... } }, { "url": "https://..." }, or { "raw": "base64..." }. Do not add a kind discriminator for new v1.0 clients.
  • Roles use ROLE_USER and ROLE_AGENT.
  • Task states use TASK_STATE_* enum values.
  • Unary SendMessage returns a SendMessageResponse object with a task member.
  • Streaming methods emit JSON-RPC response envelopes whose result contains a statusUpdate or artifactUpdate member. There is no final flag; the terminal state and stream close indicate completion.
  • Errors use JSON-RPC error with google.rpc.Status-style data entries.

Request and response examples

All requests are JSON-RPC 2.0 envelopes sent to the A2A endpoint. In Delegate mode, pass the skill name in params.metadata.skill. In Managed mode, omit it and let Runtype route the request.

SendMessage

Execute a task synchronously and wait for the result:

1{
2 "jsonrpc": "2.0",
3 "id": "req-1",
4 "method": "SendMessage",
5 "params": {
6 "message": {
7 "role": "ROLE_USER",
8 "parts": [{ "text": "What is your return policy?" }],
9 "messageId": "msg_123"
10 },
11 "metadata": { "skill": "answer_questions" }
12 }
13}

A completed task is returned inside result.task:

1{
2 "jsonrpc": "2.0",
3 "id": "req-1",
4 "result": {
5 "task": {
6 "id": "a2atask_abc123",
7 "contextId": "ctx_123",
8 "status": { "state": "TASK_STATE_COMPLETED" },
9 "artifacts": [
10 {
11 "artifactId": "artifact_1",
12 "name": "response",
13 "parts": [{ "text": "Our return policy allows returns within 30 days." }]
14 }
15 ],
16 "metadata": {}
17 }
18 }
19}

To continue a multi-turn conversation, include contextId inside params.message. To receive asynchronous webhook updates, add params.configuration.pushNotificationConfig with a webhook url.

SendStreamingMessage

Execute a task and receive incremental updates over Server-Sent Events. Each SSE data: line is a JSON-RPC response envelope with a v1.0 StreamResponse oneof.

event: task/status
data: {"jsonrpc":"2.0","id":"req-1","result":{"statusUpdate":{"taskId":"a2atask_abc123","contextId":"ctx_123","status":{"state":"TASK_STATE_WORKING"}}}}
event: task/artifact
data: {"jsonrpc":"2.0","id":"req-1","result":{"artifactUpdate":{"taskId":"a2atask_abc123","contextId":"ctx_123","artifact":{"artifactId":"artifact_1","name":"response","parts":[{"text":"Our return policy "}]},"append":false,"lastChunk":false}}}
event: task/artifact
data: {"jsonrpc":"2.0","id":"req-1","result":{"artifactUpdate":{"taskId":"a2atask_abc123","contextId":"ctx_123","artifact":{"artifactId":"artifact_1","parts":[{"text":"allows returns within 30 days."}]},"append":true,"lastChunk":true}}}
event: task/status
data: {"jsonrpc":"2.0","id":"req-1","result":{"statusUpdate":{"taskId":"a2atask_abc123","contextId":"ctx_123","status":{"state":"TASK_STATE_COMPLETED"}}}}

A task/error event is emitted instead if execution fails. The event name is useful for debugging; A2A clients should parse the JSON-RPC payload in data.

GetTask

Retrieve the current state of a task. Pass the task id, and optionally historyLength to include message history:

1{
2 "jsonrpc": "2.0",
3 "id": "req-2",
4 "method": "GetTask",
5 "params": {
6 "id": "a2atask_abc123",
7 "historyLength": 10
8 }
9}

ListTasks

List tasks for the surface. Use pageSize and pageToken for pagination:

1{
2 "jsonrpc": "2.0",
3 "id": "req-3",
4 "method": "ListTasks",
5 "params": {
6 "pageSize": 25,
7 "status": "TASK_STATE_COMPLETED",
8 "includeArtifacts": false
9 }
10}

The result includes tasks, nextPageToken, pageSize, and totalSize.

CancelTask

Cancel a running task by its id:

1{
2 "jsonrpc": "2.0",
3 "id": "req-4",
4 "method": "CancelTask",
5 "params": {
6 "id": "a2atask_abc123"
7 }
8}

SubscribeToTask

Resume streaming updates for an existing task:

1{
2 "jsonrpc": "2.0",
3 "id": "req-5",
4 "method": "SubscribeToTask",
5 "params": {
6 "id": "a2atask_abc123"
7 }
8}

Task lifecycle and errors

Every task moves through v1.0 task states:

StateDescription
TASK_STATE_SUBMITTEDTask received and queued.
TASK_STATE_WORKINGTask is actively executing.
TASK_STATE_COMPLETEDTask finished successfully.
TASK_STATE_FAILEDTask execution failed.
TASK_STATE_CANCELEDTask was canceled.
TASK_STATE_INPUT_REQUIREDThe agent needs more user input.
TASK_STATE_AUTH_REQUIREDThe agent needs authentication or authorization.
TASK_STATE_REJECTEDThe task was rejected.
TASK_STATE_UNSPECIFIEDThe upstream state was unknown.

Errors are returned as a JSON-RPC error object with a numeric code, message, and optional data array. Runtype includes a google.rpc.ErrorInfo entry with a stable reason and the A2A error domain:

1{
2 "jsonrpc": "2.0",
3 "id": "req-1",
4 "error": {
5 "code": -32002,
6 "message": "Skill not found",
7 "data": [
8 {
9 "@type": "type.googleapis.com/google.rpc.ErrorInfo",
10 "reason": "SKILL_NOT_FOUND",
11 "domain": "a2a-protocol.org"
12 }
13 ]
14 }
15}

Standard JSON-RPC codes:

CodeMeaning
-32700Parse error, such as invalid JSON.
-32600Invalid request.
-32601Method not found.
-32602Invalid params.
-32603Internal error.

A2A-specific codes:

CodeMeaning
-32001Task not found.
-32002Skill not found.
-32003Unauthorized.
-32004Rate limited.
-32005Task canceled.
-32006Invalid context.

Security considerations

A2A Surfaces support authentication and rate limiting:

  • API key authentication — Keys use the a2a_ prefix.
  • Rate limits — Configurable per key.
  • Capability scoping — Control which capabilities are exposed as skills.
  • Execution logs — Track which agents are calling your capabilities.

Use cases

Multi-agent systems

Build complex workflows by composing multiple specialized agents across different platforms.

Cross-platform collaboration

Platforms like LangChain, CrewAI, or Vercel AI SDK can discover and call your capabilities as part of larger workflows.

Cross-organization collaboration

Partner organizations can integrate their agents with yours without custom integration work.

Next steps