OmniRoute A2A Server Documentation
Agent-to-Agent Protocol v0.3 — OmniRoute as an intelligent routing agent
The A2A surface has two faces:
- JSON-RPC 2.0 at
POST /a2a(canonical entry point, defined insrc/app/a2a/route.ts). - REST under
/api/a2a/*for dashboards and tooling (status, task list, cancel).
Tasks are tracked by A2ATaskManager (src/lib/a2a/taskManager.ts, default 5-minute TTL). Skills are dispatched via A2A_SKILL_HANDLERS in src/lib/a2a/taskExecution.ts.
Agent Discovery
Section titled “Agent Discovery”curl http://localhost:20128/.well-known/agent.jsonReturns the Agent Card describing OmniRoute’s capabilities, skills, and authentication requirements.
The Agent Card’s version field is sourced from process.env.npm_package_version (see src/app/.well-known/agent.json/route.ts:13), so it stays auto-synced with package.json on every release.
Authentication
Section titled “Authentication”All /a2a requests require an API key via the Authorization header:
Authorization: Bearer YOUR_OMNIROUTE_API_KEYIf no API key is configured on the server, authentication is bypassed.
Enablement
Section titled “Enablement”A2A is controlled by the Endpoints → A2A toggle and is disabled by default. When disabled,
GET /api/a2a/status reports status: "disabled" and online: false; JSON-RPC calls to
POST /a2a return HTTP 503 with JSON-RPC error code -32000.
JSON-RPC 2.0 Methods
Section titled “JSON-RPC 2.0 Methods”message/send — Synchronous Execution
Section titled “message/send — Synchronous Execution”Sends a message to a skill and waits for the complete response.
curl -X POST http://localhost:20128/a2a \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_KEY" \ -d '{ "jsonrpc": "2.0", "id": "1", "method": "message/send", "params": { "skill": "smart-routing", "messages": [{"role": "user", "content": "Write a hello world in Python"}], "metadata": {"model": "auto", "combo": "fast-coding"} } }'Response:
{ "jsonrpc": "2.0", "id": "1", "result": { "task": { "id": "uuid", "state": "completed" }, "artifacts": [{ "type": "text", "content": "..." }], "metadata": { "routing_explanation": "Selected claude-sonnet via provider \"anthropic\" (latency: 1200ms, cost: $0.003)", "cost_envelope": { "estimated": 0.005, "actual": 0.003, "currency": "USD" }, "resilience_trace": [ { "event": "primary_selected", "provider": "anthropic", "timestamp": "..." } ], "policy_verdict": { "allowed": true, "reason": "within budget and quota limits" } } }}message/stream — SSE Streaming
Section titled “message/stream — SSE Streaming”Same as message/send but returns Server-Sent Events for real-time streaming.
curl -N -X POST http://localhost:20128/a2a \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_KEY" \ -d '{ "jsonrpc": "2.0", "id": "1", "method": "message/stream", "params": { "skill": "smart-routing", "messages": [{"role": "user", "content": "Explain quantum computing"}] } }'SSE Events:
data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"working"},"chunk":{"type":"text","content":"..."}}}
: heartbeat 2026-03-03T17:00:00Z
data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"completed"},"metadata":{...}}}tasks/get — Query Task Status
Section titled “tasks/get — Query Task Status”curl -X POST http://localhost:20128/a2a \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_KEY" \ -d '{"jsonrpc":"2.0","id":"2","method":"tasks/get","params":{"taskId":"TASK_UUID"}}'tasks/cancel — Cancel a Task
Section titled “tasks/cancel — Cancel a Task”curl -X POST http://localhost:20128/a2a \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_KEY" \ -d '{"jsonrpc":"2.0","id":"3","method":"tasks/cancel","params":{"taskId":"TASK_UUID"}}'Available Skills
Section titled “Available Skills”OmniRoute exposes 6 A2A skills wired in src/lib/a2a/taskExecution.ts::A2A_SKILL_HANDLERS. Each skill module lives in src/lib/a2a/skills/.
| Skill | ID | Description | Tags | Examples |
|---|---|---|---|---|
| Smart Routing | smart-routing |
Routes a prompt through the optimal provider/combo using OmniRoute’s combo engine + scoring | routing, providers | “Route this prompt via the best model” |
| Quota Management | quota-management |
Reports per-provider quota state, helps callers decide when to throttle/switch | quota, providers | “Check quota for anthropic” |
| Provider Discovery | provider-discovery |
Lists installed providers with capabilities, free-tier flags, OAuth status | providers, discovery | “What providers are available?” |
| Cost Analysis | cost-analysis |
Estimates cost of a request/conversation given the catalog + recent usage | cost, usage | “Estimate cost for this conversation” |
| Health Report | health-report |
Aggregates circuit breaker, cooldown, lockout state per provider | health, resilience | “Show health status of all providers” |
| List Capabilities | list-capabilities |
Returns the full 45-entry Agent Skills catalog (23 API + 21 CLI + 1 config) as a markdown table with raw SKILL.md URLs for context injection | catalog, discovery, skills | “List all OmniRoute capabilities” |
The Agent Card should be kept aligned with the live 352-provider catalog; provider counts and free/no-auth metadata are sourced from the runtime registry.
list-capabilities Skill Detail
Section titled “list-capabilities Skill Detail”The list-capabilities skill is particularly useful for external agents that need to discover what OmniRoute exposes before sending API calls. It returns a structured markdown table artifact:
| ID | Name | Category | Area | Endpoints/Commands | Raw URL || --- | --- | --- | --- | --- | --- || omni-auth | Auth & Sessions | api | auth | POST /api/auth/login, ... | https://raw.githubusercontent.com/... |...Each row includes the rawUrl column so agents can immediately fetch the full SKILL.md. The metadata.totalSkills field mirrors the catalog size (45 today). Implementation: src/lib/a2a/skills/listCapabilities.ts. See also AGENT-SKILLS.md.
REST API (auxiliary)
Section titled “REST API (auxiliary)”The JSON-RPC endpoint /a2a is the canonical A2A entry point. The REST endpoints below provide auxiliary access for dashboards and external tooling:
| Endpoint | Method | Description | Auth |
|---|---|---|---|
/api/a2a/status |
GET | Server status, registered skills | (public) |
/api/a2a/tasks |
GET | List tasks with filters | management |
/api/a2a/tasks/[id] |
GET | Get task by ID | management |
/api/a2a/tasks/[id]/cancel |
POST | Cancel running task | management |
/.well-known/agent.json |
GET | Agent Card (A2A discovery) | (public, cached 3600s) |
/api/a2a/tasks |
POST | Inbound delegation to the OmniConductor fleet (Conductor PRD RF5) | Bearer vs OMNIROUTE_API_KEY + a2aEnabled |
Inbound Conductor delegation (POST /api/a2a/tasks): external A2A agents delegate coding work to the OmniConductor fleet through OmniRoute. Body: { skill: "conductor" | "conductor-cli-<profile>", messages: [{role, content}], metadata: { conductor: { repo: { url, base_ref? }, mode?, cli?, model? } } } — only Conductor fleet skills (the ones announced on the Agent Card) are delegable; metadata.conductor.repo.url is required (the fleet works on git repos). The route translates to the hub’s POST /v1/tasks using the server-side CONDUCTOR_ORCHESTRATOR_TOKEN (fallback CONDUCTOR_HUB_TOKEN) and returns 201 { conductor_task_id, state: "submitted" }; task states flow back through the SSE→A2A mirror (RF1) and are visible via GET /api/a2a/tasks?skill=conductor.
Adding a New Skill
Section titled “Adding a New Skill”-
Create skill file:
src/lib/a2a/skills/<your-skill>.tsExport an async function
(task: A2ATask) => Promise<{ artifacts, metadata }>. Follow the shape of existing skills such assmartRouting.ts. -
Register handler: in
src/lib/a2a/taskExecution.ts, add an entry toA2A_SKILL_HANDLERS:export const A2A_SKILL_HANDLERS = {// ...existing skills"your-skill": async (task) => {const skillModule = await import("./skills/yourSkill");return skillModule.executeYourSkill(task);},}; -
Expose in Agent Card: in
src/app/.well-known/agent.json/route.ts, append to theskillsarray:{"id": "your-skill","name": "Your Skill","description": "Brief, intent-focused description","tags": ["routing", "quota"],"examples": ["Sample natural-language invocation"]} -
Write tests:
tests/unit/a2a-<your-skill>.test.ts. Cover happy path + error path. -
Document the new skill in this file’s
Available Skillstable.
Task TTL
Section titled “Task TTL”Tasks expire after ttlMinutes (default 5 min) — configured in the A2ATaskManager constructor at src/lib/a2a/taskManager.ts:82. To customize, fork the A2ATaskManager instantiation and pass a different value (e.g., new A2ATaskManager(15) for 15-minute TTL). A background interval sweeps expired tasks every 60 seconds.
Task Lifecycle
Section titled “Task Lifecycle”submitted → working → completed → failed → cancelled- Tasks expire after 5 minutes by default (see Task TTL)
- Terminal states:
completed,failed,cancelled - Event log tracks every state transition
Error Codes
Section titled “Error Codes”| Code | Meaning |
|---|---|
| -32700 | Parse error (invalid JSON) |
| -32600 | Invalid request / Unauthorized |
| -32601 | Method or skill not found |
| -32602 | Invalid params |
| -32603 | Internal error |
| -32000 | A2A endpoint is disabled |
Integration Examples
Section titled “Integration Examples”Python (requests)
Section titled “Python (requests)”import requests
resp = requests.post("http://localhost:20128/a2a", json={ "jsonrpc": "2.0", "id": "1", "method": "message/send", "params": { "skill": "smart-routing", "messages": [{"role": "user", "content": "Hello"}] }}, headers={"Authorization": "Bearer YOUR_KEY"})
result = resp.json()["result"]print(result["artifacts"][0]["content"])print(result["metadata"]["routing_explanation"])TypeScript (fetch)
Section titled “TypeScript (fetch)”const resp = await fetch("http://localhost:20128/a2a", { method: "POST", headers: { "Content-Type": "application/json", Authorization: "Bearer YOUR_KEY", }, body: JSON.stringify({ jsonrpc: "2.0", id: "1", method: "message/send", params: { skill: "smart-routing", messages: [{ role: "user", content: "Hello" }], }, }),});const { result } = await resp.json();console.log(result.metadata.routing_explanation);HagiCode
HagiCode is an agentic coding workspace: structured workflows, multi-agent execution, and Hero Dungeon views turn ideas into shipped software.
Turn ideas into polished, usable software with a smarter, faster, and more enjoyable agentic coding workflow.

- SmartStructured workflows turn intent into an executable path from idea to shipped change.
- EfficientMulti-agent workflows keep research, implementation, and review moving in parallel.
- FunHero Dungeon interfaces make long coding sessions visual, collaborative, and rewarding.