A2A partner quickstart
AXL serves the A2A protocol (v1.0, JSON-RPC binding) for any
agent with [interop] a2a = true. Three curl commands cover discovery through a
finished task.
Credentials
Ask the AXL operator for an API key (axl token create --name your-org on their side).
Every RPC call carries it as a bearer:
AUTH='Authorization: Bearer axl_key_...'
BASE='https://axl.example.com' # the deployment's PUBLIC_BASE_URL
AGENT='support' # the agent key from the card URL you were givenA JWT from a federated IdP the deployment trusts works the same way.
Discover the card
curl "$BASE/a2a/$AGENT/agent-card.json"Deployments that set A2A_WELL_KNOWN_AGENT also serve that agent's card at
$BASE/.well-known/agent-card.json. The card lists the agent's skills, the JSON-RPC
endpoint (supportedInterfaces[0].url), and the accepted security schemes.
Delegate a task
curl -X POST "$BASE/a2a/$AGENT" -H "$AUTH" -H 'Content-Type: application/json' -d '{
"jsonrpc": "2.0", "id": 1, "method": "SendMessage",
"params": { "message": {
"messageId": "m-1", "role": "ROLE_USER",
"parts": [{ "text": "Summarize the open tickets for ACME." }]
} }
}'SendMessage blocks (the spec default) until the task completes, fails, or needs your
input - bounded by a server-side wait cap of 120 seconds, after which you get the
current TASK_STATE_WORKING task and continue by polling. The completed task carries
the answer in status.message and any produced files in artifacts[] (fetch artifact
urls with the same bearer). Set "configuration": {"returnImmediately": true} to get
the task id back instantly instead of waiting.
Poll (or answer a question)
curl -X POST "$BASE/a2a/$AGENT" -H "$AUTH" -H 'Content-Type: application/json' -d '{
"jsonrpc": "2.0", "id": 2, "method": "GetTask",
"params": { "id": "TASK_ID", "historyLength": 20 }
}'A task in TASK_STATE_INPUT_REQUIRED is asking you something: status.message carries
the question as text plus a machine-readable data part. For an elicitation (the agent
wants structured input) the data part is
{"type": "elicitation", "tool": ..., "ui_component": ..., "payload": {...}} - the
payload describes the requested value (for present_choices, the options list). For
an approval (the agent wants permission for an action) it is
{"type": "approval", "tool": ..., "risk": ..., "description": ..., "answer": {...}},
where answer spells out the expected reply shape. Answer by sending a message bound
to the task:
curl -X POST "$BASE/a2a/$AGENT" -H "$AUTH" -H 'Content-Type: application/json' -d '{
"jsonrpc": "2.0", "id": 3, "method": "SendMessage",
"params": { "message": {
"messageId": "m-2", "taskId": "TASK_ID", "role": "ROLE_USER",
"parts": [{ "data": { "selected": "pro" } }]
} }
}'For approval-type questions the data part is {"decision": "approve"} or
{"decision": "deny", "reason": "..."} (bare approve / deny text works too).
Notes and limits
- Multi-turn: reuse the returned
contextIdin laterSendMessagecalls to continue the same conversation as new tasks. A terminal task never restarts - ataskIdon a terminal task is rejected; send a fresh message with thecontextId. - While a task is INPUT_REQUIRED, answer via its
taskId; acontextId-only message queues behind the parked run. ListTasks(contextId/statusfilters,pageSizeup to 100) andCancelTask({"id": ...}) round out the v1 surface.totalSizein list responses counts the returned page, not a global total.- Files in: send raw bytes (
{"raw": "<base64>", "filename": ..., "mediaType": ...}). URL file parts are rejected. - Streaming:
SendStreamingMessageandSubscribeToTaskreturn SSE where eachdata:line is a JSON-RPC response. The first frame is always a full Task snapshot;statusUpdateframes follow on state changes (task lifecycle, not token streaming), artifacts arrive asartifactUpdateframes at completion, and the stream closes once the task is terminal or needs your input (reply viaSendMessage, then re-subscribe). Push notifications are not supported. - RPC responses use content type
application/json(streaming:text/event-stream); the agent card is served asapplication/a2a+json. - Conformance:
./scripts/interop/run.shdrives this whole surface (and the MCP server) with the officiala2a-sdkandmcpPython clients against a locally booted server - seescripts/interop/README.md.
Next
- Expose an agent - the server-side configuration behind this endpoint.
- Connect an MCP client - the other interoperability surface.
- - the full method surface.