A2A Server | Rasa Documentation

Build your first agent in just a few minutes with Rasa Copilot.

On this page

New in Rasa Pro 3.17

Rasa can expose your assistant as a native Agent-to-Agent (A2A) sub-agent.

When an a2a_server block is present in endpoints.yml, rasa run registers A2A JSON-RPC routes on the same Sanic port as REST and channel webhooks. External orchestrators discover your assistant's capabilities via AgentCard, send turns over JSON-RPC, and receive A2A task lifecycle updates mapped from Rasa's dialogue state.

This page documents the sub-agent (server) side. For Rasa as an orchestrator that calls external A2A agents, see External Sub Agent and Integrating External Agents via A2A.

Basic Configuration

Add a2a_server to your endpoints.yml file. Only description is required; other fields have sensible defaults.

a2a_server:
  url: "http://localhost:5005"          # optional; inferred from --port / --interface / --ssl-certificate
  description: "My banking sub-agent"
  include_conversation_repair: true     # false → terminal completed instead of repair input_required
  task_timeout_seconds: 600             # per-task safety net; 0 disables
  a2a_message_cache_ttl_seconds: 600    # messageId replay TTL; defaults to task timeout
  max_contexts: 1000                    # in-memory session cap; 0 disables

Configuration Reference

Key Type Default Required Description
description string yes Public description for the AgentCard. Used for auto-generated and placeholder cards. Ignored when agent_card_path points to a static AgentCard JSON file.
url string inferred from --port, --interface, and --ssl-certificate no Public base URL of the Rasa server as seen by A2A orchestrators, advertised in the AgentCard (scheme, host, port, path prefix). Set explicitly when the public hostname or port differs from the local bind address (for example behind a reverse proxy or ingress).
agent_card_path string none (auto-generate from flows) no Path to a static AgentCard JSON file. When set, the file is loaded at startup and its url is overwritten with the resolved public URL.
include_conversation_repair boolean true no When true, pattern flows (for example pattern_completed) are advertised as skills and map to input_required so orchestrators know Rasa handles conversation repair. When false, the same stack state maps to completed instead.
task_timeout_seconds integer 600 no Maximum seconds each in-flight task_id may run before the server auto-cancels that task (safety net; use explicit tasks/cancel as the primary path). Timers are per task and are not extended by later message/send on the same contextId. Set to 0 to disable.
a2a_message_cache_ttl_seconds integer same as task_timeout_seconds no TTL in seconds for messageId deduplication cache entries keyed by (contextId, messageId). Set to 0 for immediate expiry (effectively disables replay).
max_contexts integer 1000 no Memory guard: maximum distinct contextId sessions retained until a terminal task outcome. input_required keeps the session reserved. Set to 0 to disable the cap.
push_notifications_enabled boolean false no When true, the AgentCard advertises push notifications and the server may POST task updates to orchestrator-supplied callback URLs. Disabled by default because callback URLs are client-controlled and can be an SSRF vector.
push_notification_allowed_hosts list of strings none no Optional hostname allowlist for push callback URLs. When set, only http/https URLs whose host matches an entry (exact or subdomain) are accepted. Loopback and private-network targets are always rejected.
auth object none (open endpoint) no Bearer JWT authentication for orchestrators calling the A2A endpoint.

Prerequisites

Rasa enforces two startup guardrails when a2a_server is configured. Both fail fast with actionable error messages.

Session configuration

When A2A is enabled, domain.session_config.start_session_after_expiry must be false.

Resumed orchestrator contextId values reuse the same Rasa sender_id. If start_session_after_expiry is true, Rasa runs action_session_start after inactivity and can silently reset slot state when the orchestrator resumes the same context.

This is validated when a model is loaded with a2a_server in endpoints.yml (for example during rasa run via load_agent). The error code is `validation.a2a_server.incompatible_session_config.

domain.yml
session_config:
  session_expiration_time: 60
  start_session_after_expiry: false  # required when a2a_server is enabled

ConversationInactive does not release an input_required A2A context. With start_session_after_expiry: false, the next message on the same contextId continues the flow after inactivity.

Sanic workers

SANIC_WORKERS=1 is required until a persistent solution for storing A2A tasks and messages ships. Multiple Sanic workers break messageId idempotency, in-flight HTTP 409 handling, orchestrator cancel, and max_contexts enforcement because these are per-worker only.

This is validated when starting the Sanic server (rasa run). The error code is `validation.a2a_server.incompatible_sanic_workers.

Scale horizontally with additional replicas and load balancer sticky routing by contextId instead of increasing Sanic workers per pod.

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

Multi-replica load balancing

When a2a_server is enabled and you run more than one Rasa replica, configure your ingress or load balancer so all A2A JSON-RPC traffic for a given contextId routes to the same pod.

A2A V1 keeps task state, messageId deduplication caches, in-flight turn queues, push notification callback registration, and max_contexts enforcement in memory on each replica. The shared tracker store (for example PostgreSQL) persists dialogue history, but A2A-specific state is not yet replicated across pods. Without sticky routing, follow-up turns on the same contextId may land on a different pod — the session can appear fresh, idempotency breaks, tasks/cancel may miss in-flight work, and push callbacks registered on one pod are invisible to others.

Requirements for multi-replica A2A:

Requirement Why
SANIC_WORKERS=1 on every replica A2A state is per-worker as well as per-pod.
Sticky routing keyed on contextId Keeps in-memory A2A state coherent for each orchestrator context.
Orchestrator reuses contextId across turns Already required for multi-step flows; stickiness depends on a stable key.

Context and task mapping

contextId in A2A requests maps to Rasa sender_id. One persistent Rasa conversation exists per orchestrator context. Each orchestrator message gets a new task_id. input_required keeps the context reserved for follow-up turns on the same contextId.