A2A lets external systems talk to a Pinkfish agent using the open Agent-to-Agent protocol (v0.3.0) — a JSON-RPC 2.0 standard for agent interoperability. When you enable A2A on an agent, any A2A-compatible client can fetch the agent’s agent card, discover its skills, and send it messages.
Use A2A when the caller is itself an agent or an A2A-aware framework. If you just want a plain “POST text, get text” webhook, use the simpler API channel instead.
Enable A2A
- Open the agent and go to the Channels tab.
- Toggle A2A on. This sets
a2aEnabled on the agent and makes its agent card publicly discoverable.
- Create an A2A API key for the agent (prefix
pf_a2a_). Keys are scoped to a single agent and are shown in full only once at creation. An agent can have up to 20 keys.
Optional settings:
- Skill allowlist — restrict which of the agent’s skills are advertised on the agent card.
- Auto-approve tools — when on, A2A calls bypass interactive tool-approval gates (use carefully).
- End-user connections — require a verified end-user token so each external caller acts as their own connected identity.
Discover the agent (agent card)
A2A clients start by fetching the agent card, which is public (gated only by the A2A toggle) and describes the agent and how to call it:
curl -s "https://<a2a-host>/a2a/{agentId}/.well-known/agent-card.json"
{
"name": "Research Assistant",
"description": "Researches topics and summarizes findings",
"protocolVersion": "0.3.0",
"url": "https://<a2a-host>/a2a/{agentId}",
"preferredTransport": "JSONRPC",
"capabilities": { },
"securitySchemes": { "ApiKey": { } },
"security": [ { "ApiKey": [] } ],
"skills": [ ],
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"]
}
The agent card is the source of truth for the call URL: clients should read the JSON-RPC endpoint from the card’s url field rather than hardcoding a host. The card is cached with a 60-second Cache-Control and an ETag.
Send a message
Call the JSON-RPC endpoint from the card’s url with the message/send method. Authenticate with the agent’s A2A key in the X-Api-Key header:
curl -s -X POST "https://<a2a-host>/a2a/{agentId}" \
-H "X-Api-Key: pf_a2a_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "message/send",
"params": {
"message": {
"kind": "message",
"messageId": "11111111-1111-1111-1111-111111111111",
"role": "user",
"parts": [
{ "kind": "text", "text": "Research the latest developments in quantum computing" }
]
}
}
}'
parts can be text, file (by URI), or structured data.
Response
A terminal reply comes back as an A2A message:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"kind": "message",
"messageId": "22222222-2222-2222-2222-222222222222",
"contextId": "auto_xyz789",
"role": "agent",
"parts": [ { "kind": "text", "text": "Here are the latest developments..." } ]
}
}
If the agent pauses (for example, it needs tool approval or an end-user connection), the result is a task with a non-terminal status.state such as input-required or auth-required. Resume by calling message/send again with the same contextId (see below).
Maintaining conversation context
A2A carries the conversation handle in contextId (the A2A equivalent of a chat ID):
- First message — omit
contextId. The server creates a chat and returns its contextId in the result.
- Follow-up messages — set
params.message.contextId to the value you got back. The agent reuses that chat and sees the full history.
A contextId is bound to the agent (and, when end-user connections are enabled, to the specific end user) that created it. A caller cannot resume a chat that belongs to a different agent or a different end user — those requests fail closed.
Authentication summary
| Header | Purpose |
|---|
X-Api-Key | The agent-scoped A2A key (pf_a2a_…). Required. Each key works for exactly one agent. |
X-Selected-Org | Optional org context. |
X-End-User-Token | A verified end-user bearer token. Required only when the agent has end-user connections enabled. |
A2A keys are per-agent and non-transferable: the key’s signed scope (a2a-agent:<agentId>) must match the agent in the URL, so one agent’s key cannot call another agent.
Supported methods
| Method | Status |
|---|
message/send | Supported (synchronous). |
message/stream | Not yet implemented — returns MethodNotFound (-32601). |
tasks/get, tasks/cancel, tasks/pushNotificationConfig/* | Not yet implemented. |
Because the current implementation is synchronous, the server returns either a terminal message or a paused task inline; clients drive multi-step flows with follow-up message/send calls that share a contextId.
Error codes
A2A uses JSON-RPC error objects. The HTTP status is 200 for protocol-level errors (the error is in the envelope), 403 for auth failures, and 404 when the agent is missing or not A2A-enabled.
| Code | Meaning |
|---|
-32700 | Parse error — invalid JSON. |
-32600 | Invalid request — jsonrpc must be "2.0". |
-32601 | Method not found. |
-32602 | Invalid params — e.g. empty message.parts. |
-32603 | Internal error — agent execution failed. |
-32010 | Unauthenticated. |
-32011 | Agent disabled (A2A not enabled). |
-32012 | Rate limited. |