Skip to content

Agent-to-Agent (A2A)

Overview

A2A (Agent-to-Agent) is an open protocol that lets one agent call another over HTTP. On Rival, you can give any agent an A2A endpoint so an external system - another platform’s agent, an orchestrator, a script - can send it tasks and read the replies, the same way a sub-agent would.

Exposing an agent over A2A is not publishing. A private agent can be reachable this way and stay off the marketplace. The endpoint is per agent, and it answers only to API keys you have explicitly granted.

Where to find it

Open the agent, open its Settings drawer, and pick the Integrate tab. Everything about A2A lives there:

  • The Expose over A2A toggle.
  • The endpoint and agent card URLs, with copy buttons.
  • A ready-to-run curl example.
  • The knowledge base and model settings for machine callers.
  • The Keys with access list.

Only an org Owner or Admin can change these settings. Other members of the organization can read them.

Enabling A2A

  1. On the Integrate tab, switch on Expose over A2A. While it’s off, the endpoint answers 404 for everyone.

  2. Copy the two URLs that appear:

    • Endpoint (POST, A2A JSON-RPC) - https://cortexone-api.rival.io/api/v1/a2a/agents/{slug}
    • Agent card (public) - https://cortexone-api.rival.io/api/v1/a2a/agents/{slug}/.well-known/agent-card.json
  3. Under Keys with access, grant an API key. Pick an existing key from Settings → API Keys (/user/api-keys), or click New key to create one and grant it in a single step. A newly created key is shown once - copy it then.

  4. Hand the endpoint URL and the key to the integrator.

Agents are addressed by slug, not id. The slug is derived from the agent’s name and shown in the URLs on the Integrate tab.

Keys and access

A2A uses ordinary organization API keys, but with one extra step: a key reaches only the agents it has been granted. A key with no grant can’t call anything, even with A2A switched on. Granting never reveals the key itself.

  • Grant and revoke keys from the Keys with access list. Each row shows when the key was last used, and flags keys that are Expired or Inactive - calls with those are refused.
  • Revoking a grant, revoking the key, or letting it expire all cut off access immediately.
  • Tasks are scoped to the key that created them. Another key granted to the same agent can’t read your tasks, even with the task id.

The agent card

The agent card is a public JSON document that describes the agent to A2A clients: its name and description, the endpoint URL, the protocol version, and that it expects a Rival API key as a bearer token. Any standard A2A client can be pointed at the card and configure itself from it.

The card deliberately publishes no skills list. Listing the agent’s tools would reveal which systems you have connected, so describe what the agent can do in its description instead - that’s what callers read.

The card returns 404 while A2A is off.

Calling the agent

The endpoint speaks standard A2A JSON-RPC. Authenticate with the granted key in the Authorization header - Bearer RIV_... and a bare RIV_... are both accepted. A user login token is not.

Terminal window
curl -X POST https://cortexone-api.rival.io/api/v1/a2a/agents/{slug} \
-H 'Authorization: Bearer RIV_YOUR_KEY' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"message/send","params":{"message":{"messageId":"m1","role":"user","parts":[{"kind":"text","text":"What can you do?"}]}}}'

Supported methods:

MethodWhat it does
message/sendSend a message and wait for the task to finish
message/streamSame, but stream progress and the reply as server-sent events
tasks/getFetch a task by id
tasks/cancelCancel a running task
tasks/resubscribeRe-attach to a running task’s stream
agent/getAuthenticatedExtendedCardFetch the card with a granted key

Reports and artifacts the agent produces are attached to the reply as their own text part, since there’s no browser on this channel. Tool calls and results appear as extra parts marked function_call and function_response.

Send an optional X-Request-Id header to make a request idempotent - a retry with the same value is treated as the same task.

What callers should expect

Multi-turn: reuse the contextId. Send back the contextId from the first response and the agent continues the same conversation, seeing the earlier turns (up to the last 40). Leave it out and every call starts fresh. Those exchanges appear in the agent’s chat history, titled “A2A · …”, and under the A2A filter on the Activity tab.

{"message": {"messageId": "m2", "role": "user",
"contextId": "<contextId from the first response>",
"parts": [{"kind": "text", "text": "and the quarter before?"}]}}

Approvals pause the task, they don’t fail it. If the agent tries something your guardrails gate, nothing is performed: the task ends as input-required and the request appears under Guardrails for you to approve or reject, marked Via A2A. The caller can’t decide it - you can. Once you have, the caller sends the same request again and the agent carries on from your decision. An approved action is never performed twice.

The agent treats the caller as external. It is told the caller is an outside, untrusted party, and connector credentials are never included in anything it sends back.

Knowledge base and model

Two settings on the Integrate tab apply only to A2A calls:

  • Knowledge base - calls over A2A carry no chat, so they read nothing unless you choose a base here. Its documents become answerable to anyone holding a granted key.
  • Model for A2A calls - machine callers can run on a different model from your own chat. Your provider and key are unchanged, only the model. Default is Same as the agent. You can also list up to 12 models callers may choose; a caller names one per request in the message metadata under the key rival/model, and anything not on the list is refused. Leaving the list empty means every call runs on the default, which is the safe setting.

Cost and limits

A2A calls don’t debit the caller. They run against the agent owner’s organization - its connected accounts, guardrails, and tool budget - and cost the same as the equivalent chat turn. The owning organization needs a positive Run Balance or calls are refused. Spend is broken down by source on the agent’s Usage tab.

Each granted key is limited per agent to 60 requests a minute and 4 tasks in flight at once. Finished tasks stay fetchable through tasks/get for 30 minutes. Request bodies are capped at 4 MiB.

Errors

StatusMeaning
401No key, or a value that isn’t a Rival API key
404No A2A-enabled agent matches, or your key isn’t granted to it. The same answer covers all three cases, so the endpoint can’t be used to enumerate agents
400The agent’s organization can’t fund requests right now
429Rate limited, or too many concurrent tasks. Check Retry-After
500The agent could not be started

At the JSON-RPC level, an unknown method returns -32601, a model the caller may not use returns -32602 with the allowed list, and a task that never existed or has aged out returns -32001.

Next steps