Skip to main content

A2A federation

Orbit agents speak the A2A (Agent-to-Agent) protocol v0.3.0, so any A2A-conformant peer can discover and task an Orbit agent, and any of your Orbit agents can task remote A2A peers (Google AgentSpace, Azure AI Foundry, Bedrock AgentCore, ServiceNow, SAP, Salesforce, Workday, IBM, custom builds — anything that implements the spec). This is the only standardised cross-vendor agent-to-agent protocol with broad ecosystem adoption (150+ orgs in production by April 2026). On Orbit it is GA and per-tenant.

What you get

  • Opt-in discovery — each agent can publish an A2A AgentCard at a stable URL once you turn discovery on (it ships off by default — see Discovery modes). Skills are derived from the agent’s tool list.
  • JSON-RPC + REST — both transports are mounted, identically authenticated.
  • HMAC signing — Stripe / Svix-style X-A2A-Signature: t=…,v1=… with a ±60s replay window.
  • Per-tenant ledger — every inbound and outbound task is persisted with full input / output / status / direction so the dashboard can render task history and you have an audit trail.
  • Peer registry — known peers are cached per agent. The platform fetches the peer’s AgentCard on add and stores it for discovery.

Discovery modes

A new agent does not serve a public AgentCard. Discovery is an explicit operator opt-in, controlled by the agent’s a2a_discovery_mode field. It has three values: Set the mode on the agent before pointing a peer at the public URL:
In the dashboard the same toggle lives under Agents → → A2A. Until you switch an agent to public, the public /.well-known/agent-card.json route returns 404 (the response shape never distinguishes “discovery off” from “agent doesn’t exist”, to avoid leaking which agents exist). tenant mode keeps the card private — it serves only the authenticated dashboard route below, never the public one.

Public surface (for peers + spec-compliant clients)

These endpoints DO NOT use the dashboard session. Tenants are resolved from a ?tenant=<schemaName> query param baked into the AgentCard URL. Get the per-agent canonical AgentCard URL from Agents → → A2A → Copy share URL in the dashboard. The URL is the only thing a peer needs.

HMAC signing

All public POST endpoints (/a2a and /a2a/tasks) require a signed request:
The signed payload is ${ts}.${rawBody}. Replay window is ±60 s. The platform uses constant-time comparison after explicit length-equality. The HMAC key is the per-org A2A secret, configurable via DEVOTEL_A2A_HMAC_SECRET. It falls back to DEVOTEL_API_SECRET_KEY for fresh deployments. The A2A spec (§4.5) lists OAuth2 / OIDC / API Key / HTTP-Auth / mTLS as supported security schemes; Orbit advertises HMAC as an APIKeySecurityScheme (header location, name X-A2A-Signature) — a spec-compatible escape hatch for peers without OAuth tooling.

Signing example (Node.js)

Discover a peer’s AgentCard

This returns a card only when the target agent’s a2a_discovery_mode is public; otherwise it returns 404.
The response follows the A2A spec. Skills are derived from the agent’s configured tools; an agent with no tools surfaces a single default skill keyed on agent.type.

Task an Orbit agent (inbound)

JSON-RPC:
REST shorthand:
Both return a Task envelope (id, status, output, etc.). Poll GET /api/v1/agents/{agentId}/a2a/tasks/{taskId} (the public REST shorthand for tasks/get, listed above) to follow progress. Like the POST endpoints, it requires an HMAC signature — sign over ${ts}. (the empty body).

Authenticated dashboard surface

These require a Clerk session and operate on the calling user’s tenant:

Add a peer

The platform fetches the AgentCard, stores it on the peer row, and surfaces the peer’s skill catalog in the dashboard. The peer URL is keyed unique per (agent_id, peer_url) — re-adding upserts.

Delegate a task to a peer (outbound)

Persists an outbound task envelope, signs the request, and forwards. The response carries the peer’s task id; the dashboard surfaces both sides of the round-trip in the agent’s A2A tab.

Tenant-scoping rules

  • The ?tenant=<schemaName> query param value MUST match ^tenant_[a-z0-9_]{8,80}$, matched case-insensitively (the regex carries the /i flag, so a mixed-case value such as Tenant_Demo is also accepted). If you validate the value client-side, use a case-insensitive test — e.g. /^tenant_[a-z0-9_]{8,80}$/i — so you don’t reject a value the server accepts. Any other value is rejected — guards against ?tenant=public or admin-schema escalation attempts.
  • Tasks are stored on the tenant’s agent_a2a_tasks table; peers on agent_a2a_peers. Both are isolated per-tenant.

Observability

Inbound + outbound tasks land in:
  • Dashboard — Agents → → A2A. Direction badges (inbound / outbound), status pills, latency. Task envelopes are stored on agent_a2a_tasks and surfaced here.
  • Audit log — peer lifecycle is recorded as agent.a2a.peer_added and agent.a2a.peer_removed. (Task delivery is not audited; inspect it via the dashboard A2A tab.)

Error codes

Spec compliance

Orbit implements A2A v0.3.0 — see the protocol spec and the reference proto. Task envelopes, AgentCard structure, JSON-RPC method names, and the part-oneof for messages all match the spec.

AP2 mandates across networks

A2A tasks a peer agent; AP2 is the adjacent protocol a peer uses to prove it was authorized to transact on someone’s behalf. When you’re federating with agents on another AP2 network — Mastercard, PayPal, or Google-side agents — a mandate can now carry an Ed25519-signed, W3C Verifiable-Credential proof instead of Orbit’s own platform-only signature, and Orbit’s mandate verifier resolves the issuer’s public key from a JWKS/DID directory (Orbit’s own by default, or a peer network’s when you point it at theirs) before trusting the mandate’s claims. That’s what lets an Orbit agent accept a mandate issued by another network’s agent, and vice versa, without the two networks sharing a secret.

See also