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
curlexample. - 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
-
On the Integrate tab, switch on Expose over A2A. While it’s off, the endpoint answers 404 for everyone.
-
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
- Endpoint (POST, A2A JSON-RPC) -
-
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. -
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.
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:
| Method | What it does |
|---|---|
message/send | Send a message and wait for the task to finish |
message/stream | Same, but stream progress and the reply as server-sent events |
tasks/get | Fetch a task by id |
tasks/cancel | Cancel a running task |
tasks/resubscribe | Re-attach to a running task’s stream |
agent/getAuthenticatedExtendedCard | Fetch 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
metadataunder the keyrival/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
| Status | Meaning |
|---|---|
401 | No key, or a value that isn’t a Rival API key |
404 | No 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 |
400 | The agent’s organization can’t fund requests right now |
429 | Rate limited, or too many concurrent tasks. Check Retry-After |
500 | The 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
- Tools, Memory & Connectors - what the agent can reach when called
- Personality & Guardrails - the approvals that pause an A2A task
- APIs - creating and managing API keys
- Website Chat Widget - put the agent on your website as a chat bubble