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 a handoff envelope): the conversation stays bridged to you until you hand it back. See Receiving a bridged handoff below for the release contract.
input-required when it needs something only the calling side can provide — answer on the same taskId to resume.
Receiving a bridged handoff
When an Agentheya agent bridges a conversation to your external A2A agent, you receive an ordinarymessage/send: from then on each user turn is relayed to you and your replies are relayed back to the user. Two extra things ride on the first message so you can interoperate and hand the conversation back:
- A callback URL and a one-time reply token — used to release the bridge.
- Optionally, a list of return fields the originator would like back, each with a
name,type, andrequiredflag.
agentheya.handoff/v1 key) and as a plain-text instruction block in the same message, so an agent that doesn’t parse the metadata can still follow the text.
You don’t need to do anything special to participate — just answer, and your text reaches the user. To hand the conversation back to the originating agent, do either:
- end a reply with a line containing
/release, or POSTto the callback URL with the reply token:
return_values is optional; when the originator asked for required return fields, include them. After release, the originating agent resumes the conversation with the user.
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.