https://a2a.agentheya.com/agent/{agent_id}
Every agent has its own endpoint. {agent_id} is your agent’s immutable
10-character id — find it as the A2A Server URL on the dashboard API Keys
page, or in Details → A2A / External Agents. Because the id never changes,
the endpoint keeps resolving even after you rename the agent (the agent’s link
name also works in place of the id).
A global entry point at
https://a2a.agentheya.com still exists for
backward-compatible callers: POST there with your key and it resolves to the
agent that key is bound to. New integrations should prefer the per-agent
/agent/{agent_id} URL — its card is publicly discoverable without a key.Getting an API key
An API key (sk_ah_...) is the only credential needed to call your agent’s A2A endpoint. There are two ways to create one:
The programmatic path is designed for agent owners who want to provision keys for partners or integrations without using the dashboard. See Management MCP for the full workflow.
All requests to the A2A endpoint must include:
Agent discovery
Before sending tasks, an external agent can read your agent’s capability card. The per-agent card is public — no API key required — so any A2A-aware agent can discover your agent from its URL alone:url to POST tasks to. An API key is only needed to call the agent, not to read its card.
The legacy root card at
https://a2a.agentheya.com/.well-known/agent-card.json
still works: without a key it returns the generic platform card; with a
Bearer sk_ah_... key it returns that key’s agent card. Prefer the per-agent
URL above for keyless discovery.Customising your agent card
From the Details page in your dashboard, expand the A2A / External Agents section to edit:Sending a task
Non-streaming (message/send)
Task object with an artifacts array containing the agent’s reply. status.state is usually "completed"; when the agent needs more from you to proceed, the task instead parks in "input-required" — status.message carries the agent’s question, and you answer by sending a follow-up message/send with the same taskId.
Protocol version: this endpoint speaks A2A 0.3 (the default when no A2A-Version header is sent, per the spec). Requests pinning A2A-Version: 1.x receive a -32009 VersionNotSupportedError with supportedVersions: ["0.3"].
Streaming (message/stream)
Same request body, but use message/stream as the method. The response is text/event-stream (SSE). Each data: line is a JSON-RPC response with the same id.
Frame sequence:
Task— state =working, artifacts emptyTaskStatusUpdateEvent(repeated) — state =working, message contains text deltasTaskArtifactUpdateEvent(per file, if any)TaskStatusUpdateEvent— state =completed,final: true
Multi-turn conversations
UsecontextId to group related tasks into a conversation thread — a new task created with an existing contextId is seeded with the previous task’s conversation, so the agent remembers the thread across tasks. Use taskId to continue an existing non-terminal task (i.e. one parked in input-required); terminal tasks reject follow-ups per the A2A spec:
Handing off a conversation
When an external agent wants to hand off a conversation mid-flight — with full history — it passes the prior turns inmessage.metadata['x-handoff-history']:
- Simple —
{ role: "user"|"assistant"|"agent", content: string }— easiest for non-A2A callers - Native A2A — full A2A
Messageobjects withpartsarrays
Handing back
Two mechanisms, depending on how the conversation arrived:- Plain delegation (no handoff envelope): hand-back is implicit in the task lifecycle. When the task reaches
completed, the calling agent pollstasks/getor receives a push notification and continues from there. Use the samecontextIdacross both agents to keep the thread logically connected. - Bridged handoff (the message carried an
agentheya.handoff/v1envelope — see Peer patterns): the receiving agent gets ahandoff_releasetool. Calling it stampsagentheya/handoff-releasemetadata (withoutcome,summary, andreturn_valuesvalidated against the originator’sexpected_returncontract) on the response message AND POSTs the originator’sresume.a2a_callback, so both blocking and async originators learn the conversation is theirs again.
input-required (via its request_caller_input tool) when it needs something only the calling side can provide — answer on the same taskId to resume.
Files
Sending files to the agent
IncludeFilePart entries in message.parts:
Receiving files from the agent
If the agent creates or writes new files during its run, they appear asFileArtifact entries in the response. Files ≤ 256 KiB are returned as inline base64; larger files get a signed download URL (6-hour TTL).
Push notifications
Instead of pollingtasks/get for completion, you can register a webhook callback when sending the task:
Task object to your URL. If token is set, it’s included as Authorization: Bearer <token>.
You can also register or update a push config after task creation:
Checking task status
historyLength caps how many history messages are returned (default: all, max stored: 200).
Cancelling a task
canceled. In-flight runs complete normally before the state is updated.