# Build Your First Agent in Just a Few Minutes with Rasa Copilot

## New in Rasa Pro 3.17

Rasa can expose your assistant as a native [Agent-to-Agent (A2A)](https://a2a-protocol.org/latest/) sub-agent.

## Overview

In the [orchestrator model](/content/docs/pro/build/integrating-external-agents/index.html), Rasa calls **external** A2A agents from flows. This guide covers the **inverse role**: your Rasa assistant runs as an A2A **sub-agent** that an external orchestrator discovers and invokes.

With an `a2a_server` block in `endpoints.yml`, `rasa run` exposes the A2A protocol on the **same port** as REST and channel webhooks. The orchestrator fetches your `AgentCard`, sends user turns over JSON-RPC, and receives structured task lifecycle updates mapped from Rasa's dialogue state.

For full configuration reference, see [A2A Server](/content/docs/reference/integrations/a2a-server/index.html).

## Prerequisites

Before enabling A2A server mode:

1. **Train a CALM assistant** with the user-facing flows you want to expose as skills.
2. **Set session config** in `domain.yml` — `start_session_after_expiry` must be `false` so resumed orchestrator contexts do not trigger `action_session_start` and reset slots.

```yaml
session_config:
  session_expiration_time: 60
  start_session_after_expiry: false  # required when a2a_server is enabled
```

3. **Plan for single-worker deployment** — set `SANIC_WORKERS=1` until Redis-backed A2A stores ship. Scale with additional replicas and [sticky load balancing by `contextId`](/content/docs/reference/integrations/a2a-server/#multi-replica-load-balancing/index.html) instead of multiple Sanic workers per pod.

## Step 1 — Add `a2a_server` to `endpoints.yml`

Only `description` is required. Add a public `url` when orchestrators reach Rasa through a load balancer or ingress rather than `localhost`.

```yaml
a2a_server:
  url: "http://localhost:5005"
  description: "Banking assistant for transfers and appointments"
  include_conversation_repair: true
```

If you use custom actions, include your `action_endpoint` in the same file as usual.

## Step 2 — Start the Server

Run Rasa with a single Sanic worker and your trained model:

```bash
SANIC_WORKERS=1 rasa run -m models/your-model.tar.gz --endpoints endpoints.yml
```

To serve HTTPS directly, pass the same [`rasa run` SSL flags](/content/docs/reference/api/command-line-interface/#rasa-run/index.html) used for REST and channels — not an `a2a_server.tls` block in `endpoints.yml`:

```bash
SANIC_WORKERS=1 rasa run \
  --ssl-certificate /certs/server.pem \
  --ssl-keyfile /certs/server-key.pem \
  -m models/your-model.tar.gz \
  --endpoints endpoints.yml
```

When Rasa sits behind a reverse proxy or ingress, set `a2a_server.url` to the public `https://...` base URL orchestrators use. See [TLS](/content/docs/reference/integrations/a2a-server/#tls/index.html) in the A2A server reference.

### Multi-replica Deployments

If you run more than one Rasa pod, configure your ingress or load balancer to route all A2A traffic for a given `contextId` to the same replica. Without this, follow-up turns, `messageId` deduplication, `tasks/cancel`, and push callbacks can break because A2A state is in-memory per pod.

## Step 3 — Verify the Server is Ready

Confirm the model is loaded and the AgentCard is available:

```bash
# Model loaded (no auth configured)
curl http://localhost:5005/status

# Model loaded (Rasa auth token configured)
curl -H "Authorization: Bearer <rasa-auth-token>" http://localhost:5005/status

# Capability discovery (no A2A bearer auth configured)
curl http://localhost:5005/.well-known/agent-card.json

# Capability discovery (A2A bearer auth configured via a2a_server.auth)
curl -H "Authorization: Bearer <jwt>" http://localhost:5005/.well-known/agent-card.json
```

The AgentCard lists your user flows as skills. The primary signal an orchestrator uses when deciding which sub-agent to route a task to is the AgentCard.

## Step 4 — Point Your Orchestrator at the Endpoint

Configure your orchestrator to use the `url` from the AgentCard.

### Example `message/send` Request

```bash
curl -X POST http://localhost:5005/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": "req-1",
    "method": "message/send",
    "params": {
      "message": {
        "role": "user",
        "parts": [{"kind": "text", "text": "I want to book an appointment"}],
        "messageId": "msg-1",
        "contextId": "ctx-user-42"
      }
    }
  }'
```

## Step 5 — Read Structured Results from Completed Flows

Each orchestrator message creates a new A2A `task_id`. Rasa maps dialogue state to A2A task states: `working`, `input_required`, `completed`, `failed`, `canceled`, `rejected`, and `auth_required`.
