Skip to main content
AgentHeya agents expose a fully spec-compliant Google A2A v0.3.0 endpoint. Any A2A-compatible agent framework — LangGraph, CrewAI, Claude Managed Agents, custom orchestrators — can discover your agent, send tasks, receive streaming responses, and hand off conversations with full history. A2A endpoint: 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:
The card describes your agent’s name, description, supported input/output formats, capabilities (streaming, push notifications), and example queries, plus the JSON-RPC 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)

The response is a complete 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:
  1. Task — state = working, artifacts empty
  2. TaskStatusUpdateEvent (repeated) — state = working, message contains text deltas
  3. TaskArtifactUpdateEvent (per file, if any)
  4. TaskStatusUpdateEvent — state = completed, final: true

Multi-turn conversations

Use contextId 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:
The agent sees all prior turns in the task’s history, so follow-up questions have full context.

Handing off a conversation

When an external agent wants to hand off a conversation mid-flight — with full history — it passes the prior turns in message.metadata['x-handoff-history']:
Two formats accepted:
  • Simple{ role: "user"|"assistant"|"agent", content: string } — easiest for non-A2A callers
  • Native A2A — full A2A Message objects with parts arrays
Up to 100 prior turns are accepted. They’re prepended into the task’s history so the receiving agent has the full conversation context before it responds.

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 polls tasks/get or receives a push notification and continues from there. Use the same contextId across 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.
The receiving agent can also park the task in 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 ordinary message/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, and required flag.
These are provided both as structured message metadata (under the 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
  • POST to 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

Include FilePart entries in message.parts:
Or by URL:
Files are saved to the agent’s storage and made available to its file-reading tools automatically.

Receiving files from the agent

If the agent creates or writes new files during its run, they appear as FileArtifact 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 polling tasks/get for completion, you can register a webhook callback when sending the task:
When the task completes (or fails), we POST the full 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

Best-effort — marks the task as canceled. In-flight runs complete normally before the state is updated.

Error codes


A2A and the Inbox

Every A2A conversation is automatically mirrored to the agent owner’s Inbox dashboard page. The owner can read the full thread and take over the conversation directly from there using the standard inbox/handoff UI.