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

# Agent-to-agent (A2A)

> Expose an Agent Studio agent to external orchestrators through the A2A protocol, so other agents can discover it and send it messages.

The [A2A protocol](https://a2a-protocol.org/latest/) (Agent2Agent) is an open standard for agents that talk to other agents.
With A2A enabled, an Agent Studio agent publishes an agent card that describes what it does, and answers messages over JSON-RPC.
Any A2A client can then route questions to it: an orchestrator built with the a2a-sdk or Google's Agent Development Kit, or another agent.

An **orchestrator** is an app or agent that coordinates work across agents and decides which agent handles each request.
It uses an A2A client to send messages to your Agent Studio agent.

## Benefits

* **Discovery.** Your agent publishes a card with its name, description, skills, and supported authentication methods. Orchestrators read it to decide which tasks fit the agent.
* **Delegation.** An orchestrator sends the agent one message and gets one answer back, over a standard transport, without an Algolia-specific client.
* **Reuse.** The same published agent serves your chat widget through `/completions` and your orchestrator through A2A. Model, guardrails, and caching work the same way on both. For tool limitations, see [Design your agent for A2A](#design-your-agent-for-a2a). A2A requests and responses appear in your conversations and analytics.

## Request flow

```mermaid theme={"system"}
sequenceDiagram
    participant Orchestrator as Your orchestrator
    participant API as Agent Studio API
    participant Agent as Your agent

    Note over Orchestrator,API: Discovery (once)
    Orchestrator->>API: GET /.well-known/agent-card.json
    API-->>Orchestrator: Agent card (skills, RPC URL, security schemes)

    Note over Orchestrator,Agent: Per message
    Orchestrator->>API: POST /rpc  SendMessage
    API->>Agent: Run the agent on the message
    Agent-->>API: Final text answer
    API-->>Orchestrator: JSON-RPC result: one Message
```

Each message request to Agent Studio starts an independent **turn**:
one text message from the client followed by one text reply from the agent.
Agent Studio doesn't support conversation continuation, task lifecycles, streaming, or push notifications through A2A.

## Enable A2A on an agent

**1. Publish the agent**

A2A only serves published agents.
Publish your agent from the Agent Studio dashboard before you enable A2A.
To learn more, see [Get started with the Agent Studio dashboard](https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/dashboard.md).

**2. Turn on A2A**

Select your agent in the [Agent Studio dashboard](https://dashboard.algolia.com/generativeAi/agent-studio/agents).

In **Advanced settings**, turn on **Agent-to-agent (A2A)** and click **Configure**.
Copy the **agent card URL** to use in your orchestrator.

**3. Describe the agent for orchestrators**

In the same panel, write the card content that orchestrators read to route tasks:

* **Name and description.** They default to the agent's own name and description. Override them to describe the agent in business terms, including what it doesn't handle.
* **Skills.** Add one skill per capability, with a short name, a description, tags, and example queries. Click **Generate** to let the agent's model draft a description or suggest skills from the agent's instructions and tools, then edit before you save.

Save the agent. The card reflects your changes on the next request.

## Endpoints

Both endpoints use your app's Agent Studio host.
A2A clients retrieve the agent card, then read the RPC URL from its `supportedInterfaces` property.

**Agent card**

Copy the exact agent card URL from the Algolia dashboard and give it to your orchestrator.
The orchestrator uses this endpoint to discover the agent's capabilities and connection details.

The following URL shows the endpoint format:

```txt Agent card theme={"system"}
GET https://$ALGOLIA_APPLICATION_ID.algolia.net/agent-studio/1/a2a/apps/$ALGOLIA_APPLICATION_ID/agents/$AGENT_ID/.well-known/agent-card.json
```

**RPC**

Use this endpoint to send messages to the agent.
A2A SDK clients read the URL from the agent card automatically.
If you send requests directly without an SDK, use the following endpoint:

```txt RPC theme={"system"}
POST https://$ALGOLIA_APPLICATION_ID.algolia.net/agent-studio/1/a2a/apps/$ALGOLIA_APPLICATION_ID/agents/$AGENT_ID/rpc
```

For a complete request example, see [Send a message](#send-a-message).

Both endpoints return HTTP `404` if the agent doesn't exist, isn't published, or doesn't have A2A enabled.

## Authentication

Both endpoints accept an Algolia API key with the `search` ACL, or a [secured API key](https://www.algolia.com/doc/guides/security/api-keys/how-to/user-restricted-access-to-data.md) scoped to the agent's indices.
The application is part of the URL, so the `X-Algolia-Application-Id` header is optional. If you send it, it must match the application in the path.

Send the key in one of two ways. The card advertises both as security schemes.

```sh Algolia header theme={"system"}
curl "https://$ALGOLIA_APPLICATION_ID.algolia.net/agent-studio/1/a2a/apps/$ALGOLIA_APPLICATION_ID/agents/$AGENT_ID/.well-known/agent-card.json" \
  -H "X-Algolia-API-Key: $ALGOLIA_SEARCH_API_KEY"
```

```sh Bearer token theme={"system"}
curl "https://$ALGOLIA_APPLICATION_ID.algolia.net/agent-studio/1/a2a/apps/$ALGOLIA_APPLICATION_ID/agents/$AGENT_ID/.well-known/agent-card.json" \
  -H "Authorization: Bearer $ALGOLIA_SEARCH_API_KEY"
```

Use the Bearer form for orchestrators that can't send custom headers.
If you send both, they must carry the same key.
A request without a credential gets a `401` with a `WWW-Authenticate: Bearer` challenge.

> [!WARNING]
>
> Use a search-only key or a secured key.
> Never use your Admin API key.
> [Agent Studio's approved-domain list](https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/approved-domains.md) restricts which websites can send browser requests to the completions API endpoint.
> This restriction doesn't apply to A2A endpoints:
> anyone with an accepted API key can call the agent, subject to its rate limits.

## Send a message

A2A clients handle the protocol for you.
The following example uses the [A2A Python SDK](https://github.com/a2aproject/a2a-python)
to discover the agent from its card and send one message.
To run it, first install the SDK and HTTPX:

```sh Command line icon=square-terminal theme={"system"}
pip install a2a-sdk httpx
```

Replace `CARD_URL` with the agent card URL copied from the Algolia dashboard.

Then run the example:

```python Python icon=code expandable theme={"system"}
import asyncio
import uuid

import httpx
from a2a.client import A2ACardResolver, ClientFactory
from a2a.client.client import ClientConfig
from a2a.types import Message, Part, Role, SendMessageRequest

CARD_URL = "https://ALGOLIA_APPLICATION_ID.algolia.net/agent-studio/1/a2a/apps/ALGOLIA_APPLICATION_ID/agents/AGENT_ID/.well-known/agent-card.json"
API_KEY = "ALGOLIA_SEARCH_API_KEY"


async def main() -> None:
    base_url, _, card_path = CARD_URL.partition("/1/a2a/")
    headers = {"X-Algolia-API-Key": API_KEY}

    async with httpx.AsyncClient(headers=headers, timeout=120) as http:
        resolver = A2ACardResolver(http, base_url, agent_card_path=f"/1/a2a/{card_path}")
        card = await resolver.get_agent_card()
        print("Skills:", [skill.name for skill in card.skills])

        client = ClientFactory(ClientConfig(httpx_client=http)).create(card)
        request = SendMessageRequest(
            message=Message(
                message_id=str(uuid.uuid4()),
                role=Role.ROLE_USER,
                parts=[Part(text="Recommend three classic science fiction movies from the 1980s.")],
            )
        )
        async for response in client.send_message(request):
            if response.HasField("message"):
                print("".join(part.text for part in response.message.parts))


asyncio.run(main())
```

Without an SDK, send the JSON-RPC request yourself.
Every RPC request needs the `A2A-Version: 1.0` header. SDK clients add it for you:

```sh Command line icon=square-terminal theme={"system"}
curl "https://$ALGOLIA_APPLICATION_ID.algolia.net/agent-studio/1/a2a/apps/$ALGOLIA_APPLICATION_ID/agents/$AGENT_ID/rpc" \
  -X POST \
  -H 'Content-Type: application/json' \
  -H 'A2A-Version: 1.0' \
  -H "X-Algolia-API-Key: $ALGOLIA_SEARCH_API_KEY" \
  --data-raw '{
    "jsonrpc": "2.0",
    "id": "1",
    "method": "SendMessage",
    "params": {
      "message": {
        "messageId": "'"$(uuidgen)"'",
        "role": "ROLE_USER",
        "parts": [{ "text": "Recommend three classic science fiction movies from the 1980s." }]
      }
    }
  }'
```

The response is a JSON-RPC result with one message from the agent:

```json Response icon=braces theme={"system"}
{
  "jsonrpc": "2.0",
  "id": "1",
  "result": {
    "message": {
      "messageId": "alg_msg_knCtTn8KYJCw1F0H",
      "contextId": "985f1c2a-ca8a-4012-87a2-996d04fa6715",
      "role": "ROLE_AGENT",
      "parts": [{ "text": "Three picks from the 1980s: ..." }]
    }
  }
}
```

The server generates `contextId` for each turn, and it's informational only. Don't send it back: every message is a new turn.

## Use the agent from an orchestrator

Orchestration frameworks with A2A support treat your agent as a remote sub-agent: they read the card, and their router delegates to it when a task matches the card's description and skills.
The following example uses Google's Agent Development Kit (ADK) to route requests to your Agent Studio agent.
To run it, first install ADK with A2A support:

```sh Command line icon=square-terminal theme={"system"}
pip install "google-adk[a2a]"
```

Replace `CARD_URL` with the agent card URL copied from the Algolia dashboard.
The concierge uses a Gemini model, so set the `GOOGLE_API_KEY` environment variable to a [Google AI Studio](https://aistudio.google.com/apikey) key, or follow the [ADK quickstart](https://adk.dev/get-started/quickstart/) to use another model.

> [!NOTE]
>
> The ADK A2A integration is experimental.
> Check the [ADK documentation](https://adk.dev/a2a/quickstart-consuming/) for API changes.

The concierge agent answers general questions itself and delegates movie questions to the Agent Studio agent:

```python Python icon=code expandable theme={"system"}
import asyncio

import httpx
from google.adk.agents import LlmAgent
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.genai import types

CARD_URL = "https://ALGOLIA_APPLICATION_ID.algolia.net/agent-studio/1/a2a/apps/ALGOLIA_APPLICATION_ID/agents/AGENT_ID/.well-known/agent-card.json"
API_KEY = "ALGOLIA_SEARCH_API_KEY"


async def main() -> None:
    async with httpx.AsyncClient(headers={"X-Algolia-API-Key": API_KEY}, timeout=120) as http:
        movies_agent = RemoteA2aAgent(
            name="movies_agent",
            description="Recommends movies from the catalog.",
            agent_card=CARD_URL,
            httpx_client=http,
        )
        concierge = LlmAgent(
            name="concierge",
            model="gemini-2.5-flash",
            instruction="Transfer movie questions to movies_agent. Answer other questions yourself.",
            sub_agents=[movies_agent],
        )

        sessions = InMemorySessionService()
        runner = Runner(agent=concierge, app_name="concierge", session_service=sessions)
        session = await sessions.create_session(app_name="concierge", user_id="user-1")
        message = types.Content(role="user", parts=[types.Part(text="Recommend a classic 1980s science fiction movie.")])

        async for event in runner.run_async(user_id="user-1", session_id=session.id, new_message=message):
            if event.content and event.content.parts and event.content.parts[0].text:
                print(f"{event.author}: {event.content.parts[0].text}")


asyncio.run(main())
```

The `httpx_client` sends your Algolia API key when ADK retrieves the agent card and sends messages to the agent.

## Design your agent for A2A

A2A callers only receive the agent's text reply.
Keep these points in mind when you point an orchestrator at an agent that also serves a chat widget:

* **Answer in text.** Results the agent shows as product cards in the widget don't travel over A2A. Instruct the agent to list results in its reply, for example as a numbered list with a title and one sentence each.
* **Avoid client-side tools and approvals.** For A2A requests, Agent Studio excludes [client-side tools](https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/tools/client-side-tools.md) because orchestrators can't run them. Turns that require tool approval fail without returning a final answer.
* **Give the agent a way to stop.** When the index can't answer a question, the agent can keep searching until it reaches its [`max_iterations`](https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/agent-configuration) limit (**Max steps per completion** in the dashboard), which fails the turn. Instruct the agent to answer with what it found, or to say it can't, when a search returns nothing. Make sure the attributes orchestrators ask about are searchable. Raise the limit only for agents that need longer tool chains: each step is a billed model call.
* **Write the card for a router.** Orchestrators pick agents by reading skills and descriptions. Describe business capabilities and their limits, not implementation details.

## Caching, conversations, and analytics

A2A turns behave like a first `/completions` turn:

* When you enable caching, the [completion cache](https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/caching.md) answers identical messages to the same agent without a model call. The response carries an `X-Cache` header.
* Agent Studio stores each turn as a one-turn [conversation](https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/conversations.md).
  A2A activity also appears in [Agent Studio analytics](https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/analytics.md).
* [Guardrails](https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/guardrails.md) and rate limits apply as they do on `/completions`.

## Errors

Failures before the request reaches the agent are plain HTTP errors.
Failures after that are an HTTP `200` with a JSON-RPC error object.

| Condition | Response |
| - | - |
| Missing credential or unsupported `Authorization` scheme | `401` with `WWW-Authenticate: Bearer` |
| Header key and Bearer key differ, or `X-Algolia-Application-Id` differs from the path | `400` |
| Application not enabled for A2A, or key without the `search` ACL | `403` |
| Agent not found, not published, or A2A not enabled. Also a secured key that can't search the agent's indices | `404` |
| Rate limit exceeded | `429` with `Retry-After` |
| Missing or unsupported `A2A-Version` header | JSON-RPC `-32009` |
| Invalid message: non-user role, empty text, over-long `messageId`, `contextId` set | JSON-RPC `-32602` |
| Task, streaming, or push-notification methods | JSON-RPC `-32004` or `-32003` |
| Non-text part, or `acceptedOutputModes` without a text mode | JSON-RPC `-32005` |
| Agent failed, timed out, produced no text, or stopped on a tool approval | JSON-RPC `-32603` |

A `-32603` error carries a fixed message. Check the agent's conversations and your provider's logs for the cause: a step-limit or provider error on the agent's side shows up there.
A turn has about 100 seconds to answer, covering agent setup and the model call.
A turn that takes longer fails with `-32603` and Agent Studio discards it: it doesn't create a conversation, and the turn doesn't appear in analytics.
Your LLM provider may still bill the interrupted model call.

## Further reading

* [A2A protocol specification](https://a2a-protocol.org/latest/specification/). The protocol Agent Studio implements, with the full JSON-RPC method list.
* [Integrate Agent Studio](https://www.algolia.com/doc/guides/algolia-ai/agent-studio/how-to/integration.md). Call the same agent from your frontend through the completions API.
