How A2A works

Use the Agent-to-Agent (A2A) protocol to let external AI agents discover and invoke capabilities through an Agent Card and JSON-RPC transport.

Runtype A2A Surfaces use the A2A Protocol v1.0 wire format. When you integrate with a v1.0 Surface, use PascalCase method names, ProtoJSON enum values, and member-presence message parts.

Discover capabilities

Fetch the Agent Card to discover a Surface’s identity, skills, interfaces, and authentication schemes:

$GET https://api.runtype.com/v1/products/PRODUCT_ID/surfaces/SURFACE_ID/a2a/.well-known/agent-card.json

Replace PRODUCT_ID and SURFACE_ID with the IDs for the Product and Surface. The discovery endpoint is public, so you can fetch the Agent Card without authentication.

The Agent Card describes the Product’s identity, skills, supported interfaces, and authentication schemes. Use its information to prepare an authenticated invocation.

Invoke capabilities

After you fetch the Agent Card, send JSON-RPC requests to the A2A endpoint:

$POST https://api.runtype.com/v1/products/PRODUCT_ID/surfaces/SURFACE_ID/a2a

The endpoint accepts these v1.0 methods:

  • SendMessage: Runs a task synchronously.
  • SendStreamingMessage: Streams task updates over Server-Sent Events (SSE).
  • GetTask: Retrieves a task by ID.
  • ListTasks: Lists tasks with cursor pagination.
  • CancelTask: Cancels a running task.
  • SubscribeToTask: Resumes an SSE stream for a task.

The endpoint also accepts legacy slash-string method aliases from A2A 0.3 clients. Target the v1.0 protocol to use the PascalCase method names.

Authenticate requests with one of these headers:

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

Replace YOUR_API_KEY with an A2A Surface key.

Route requests with orchestration

When you enable Managed orchestration on an A2A Surface, omit params.metadata.skill from the request. The orchestrator evaluates the request against the Product’s Capabilities and routes it to a matching Flow or Agent.

The Managed request flow follows these steps:

  1. The external agent sends a natural-language request without params.metadata.skill.
  2. The orchestrator evaluates the request against the Product’s Capabilities.
  3. The orchestrator routes the request to a matching Flow or Agent.
  4. Runtype returns the A2A task response.

Choose Delegate mode to expose each Capability as a separate skill. The calling agent selects the skill. Choose Managed mode to expose one product-level entry point.

Follow the protocol format

The Runtype A2A Surface follows the A2A Protocol v1.0. Follow these wire-format conventions:

  • JSON-RPC 2.0: Send every method in a JSON-RPC 2.0 envelope.
  • Message parts: Encode each part by member presence: { "text": "Hello" }, { "data": { "key": "value" } }, { "url": "https://example.com/file.pdf" }, or { "raw": "YWJj" }. Do not add a kind discriminator.
  • Roles: Set the role to ROLE_USER or ROLE_AGENT.
  • Task states: Use TASK_STATE_* enum values.
  • Unary SendMessage: Read the returned task from the task member of the SendMessageResponse object.
  • Streaming methods: Parse each JSON-RPC response envelope from the SSE stream. The result contains a statusUpdate or artifactUpdate member.
  • Completion: Use the terminal task state and stream closure to detect completion. Streaming responses do not include a final flag.
  • Errors: Read JSON-RPC error objects with google.rpc.Status-style data entries.

Review request and response examples

Use these examples as a reference for JSON-RPC 2.0 requests and responses. In Delegate mode, pass the skill name in params.metadata.skill. In Managed mode, omit the metadata object so Runtype routes the request.

SendMessage

Send a synchronous request and wait for the returned task:

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 successful response wraps the task in result.task:

1{
2 "jsonrpc": "2.0",
3 "id": "req-1",
4 "result": {
5 "task": {
6 "id": "a2atask_01jq8z7m1nd4e5f6g7h8j9k0p1",
7 "contextId": "ctx_01jq8z7m2pe5f6g7h8j9k0p1q2",
8 "status": { "state": "TASK_STATE_COMPLETED" },
9 "artifacts": [
10 {
11 "artifactId": "artifact_01jq8z8n2pe5f6g7h8j9k0p1q2",
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 in params.message. To receive asynchronous webhook updates, add params.configuration.pushNotificationConfig with a webhook url.

SendStreamingMessage

Send SendStreamingMessage to receive incremental updates over Server-Sent Events (SSE). Each data: line contains a JSON-RPC response envelope with a v1.0 StreamResponse oneof.

Use this stream as a reference:

event: task/status
data: {"jsonrpc":"2.0","id":"req-1","result":{"statusUpdate":{"taskId":"a2atask_01jq8z7m1nd4e5f6g7h8j9k0p1","contextId":"ctx_01jq8z7m2pe5f6g7h8j9k0p1q2","status":{"state":"TASK_STATE_WORKING"}}}}
event: task/artifact
data: {"jsonrpc":"2.0","id":"req-1","result":{"artifactUpdate":{"taskId":"a2atask_01jq8z7m1nd4e5f6g7h8j9k0p1","contextId":"ctx_01jq8z7m2pe5f6g7h8j9k0p1q2","artifact":{"artifactId":"artifact_01jq8z8n2pe5f6g7h8j9k0p1q2","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_01jq8z7m1nd4e5f6g7h8j9k0p1","contextId":"ctx_01jq8z7m2pe5f6g7h8j9k0p1q2","artifact":{"artifactId":"artifact_01jq8z8n2pe5f6g7h8j9k0p1q2","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_01jq8z7m1nd4e5f6g7h8j9k0p1","contextId":"ctx_01jq8z7m2pe5f6g7h8j9k0p1q2","status":{"state":"TASK_STATE_COMPLETED"}}}}

The task/error event reports execution failures. Parse the JSON-RPC payload in data; use the event name for debugging. The stream closes after the terminal status.

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_01jq8z7m1nd4e5f6g7h8j9k0p1",
7 "historyLength": 10
8 }
9}

ListTasks

List tasks for the Surface. Use pageSize and pageToken for cursor 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 passing its id:

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

SubscribeToTask

Resume streaming updates for an existing task by passing its id:

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

Track task lifecycle and errors

A task reports one of these v1.0 states:

StateDescription
TASK_STATE_SUBMITTEDTask received and queued.
TASK_STATE_WORKINGTask execution is in progress.
TASK_STATE_COMPLETEDTask execution finished successfully.
TASK_STATE_FAILEDTask execution failed.
TASK_STATE_CANCELEDTask execution was canceled.
TASK_STATE_INPUT_REQUIREDTask requires additional input.
TASK_STATE_AUTH_REQUIREDTask requires authentication or authorization.
TASK_STATE_REJECTEDTask was rejected.
TASK_STATE_UNSPECIFIEDThe upstream state is unknown.

Runtype returns errors as JSON-RPC error objects with a numeric code, a message, and an optional data array. The data array includes a google.rpc.ErrorInfo entry with a stable reason and the a2a-protocol.org 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}

Use these standard JSON-RPC error codes when you handle protocol errors:

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

Use these A2A-specific error codes when you handle Surface errors:

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

Protect the A2A Surface

Use these controls to protect access to your A2A Surface:

  • API key authentication: Authenticate with an A2A Surface key that starts with a2a_.
  • Rate limits: Configure limits for each key.
  • Capability scoping: Limit the Capabilities that each key can invoke.
  • Execution logs: Review A2A task execution records.

Keep the Agent Card public for discovery. Require a valid A2A key for JSON-RPC invocation.

Apply A2A

Use A2A to compose workflows from specialized agents across platforms:

Compose multi-agent systems

Compose complex workflows from specialized agents across different platforms.

Connect platforms

Connect platforms such as LangChain, CrewAI, and Vercel AI SDK. These platforms can discover and invoke your Capabilities as part of larger workflows.

Collaborate across organizations

Share an A2A endpoint with a partner organization. Its agents can invoke your Capabilities without a custom integration.

Next steps