> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pinkfish.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# A2A (Agent-to-Agent) protocol

> Expose a Pinkfish agent over the open A2A protocol so external agents can discover and call it

**A2A** lets external systems talk to a Pinkfish agent using the open [Agent-to-Agent protocol](https://a2a-protocol.org/) (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](/api-reference/platform/agent-api-channel) instead.

## Enable A2A

1. Open the agent and go to the **Channels** tab.
2. Toggle **A2A** on. This sets `a2aEnabled` on the agent and makes its agent card publicly discoverable.
3. 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:

```bash theme={null}
curl -s "https://<a2a-host>/a2a/{agentId}/.well-known/agent-card.json"
```

```json theme={null}
{
  "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"]
}
```

<Note>
  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`.
</Note>

## 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:

```bash theme={null}
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**:

```json theme={null}
{
  "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.                                |

## Related

* [Agent API channel](/api-reference/platform/agent-api-channel) — the simpler plain-HTTP surface for one agent.
* [Agent Management API](/api-reference/platform/agent-management) — create, update, list, and invoke agents.
* [Channels tab](/agents/channels) — enabling channels from the agent builder UI.
