Product

Product overview Agents Automations Code (Beta) Integrations Governed Runtime Security

Apps

Windows app Android app Inside other AI apps

Solutions

Finance Sales Operations People and HR Marketing Enterprise

Resources

Pricing Docs Academy AI governance Blog Compare Support
Book a demo Sign in
Docs / Connect / A2A

Renly as an A2A agent

Renly speaks the Agent2Agent (A2A) protocol, version 1.0. Another agent can discover Renly, hand a task to one of your organization's Renly agents, and follow it to the result. Changes follow your organization's approval rules, and anything that needs approval, including every send, waits for a person to approve it in Renly.

At a glance. Agent Card: https://renly.ai/.well-known/agent-card.json. Endpoint: https://renly.ai/api/a2a (JSON-RPC 2.0). Protocol version 1.0. Auth: an agent access key as a Bearer token. Streaming: yes (Server-Sent Events). Push notifications: not supported.

How it works

  • One endpoint, many agents. Each Renly agent you can use is an interface on the same endpoint, addressed by putting its agent id in the request's tenant field.
  • A task is a Renly conversation. Each task's contextId is an ordinary Renly chat owned by the person whose key made the call, so the work is visible in Renly. The task's metadata.renly.conversationUrl links to it.
  • Approvals stay with people. When the agent proposes an action that needs approval, the task moves to TASK_STATE_INPUT_REQUIRED and an approval card waits in that conversation. The calling agent cannot approve. When a person approves in Renly, the task continues; if they decline, it ends TASK_STATE_REJECTED.
  • Same governance as chat. The agent runs as the key's owner, with their connectors and your organization's policies, and every action is recorded as evidence.

Connect in four steps

  1. Fetch the public card. It needs no key and names no customer agents.
    curl https://renly.ai/.well-known/agent-card.json
  2. Create an access key with the a2a scope. In Renly, open the user menu, choose Agent access, and create a key under New key. Copy it: it is shown once. See agent access keys.
  3. List the agents you can call with GetExtendedAgentCard. Each agent appears as a supportedInterfaces entry whose tenant is the agent id, and as a skill with the same id.
    curl -s https://renly.ai/api/a2a \
      -H "Authorization: Bearer $RENLY_KEY" \
      -H "Content-Type: application/json" \
      -H "A2A-Version: 1.0" \
      -d '{"jsonrpc":"2.0","id":1,"method":"GetExtendedAgentCard","params":{}}'
  4. Send a task with SendMessage, then follow it with GetTask (below).

SendMessage

Put the agent id in params.tenant. If your key is limited to a single agent, you can leave tenant out. The message must have role: "ROLE_USER", a messageId you choose, and at least one part.

curl -s https://renly.ai/api/a2a \
  -H "Authorization: Bearer $RENLY_KEY" \
  -H "Content-Type: application/json" \
  -H "A2A-Version: 1.0" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "SendMessage",
    "params": {
      "tenant": "AGENT_ID",
      "message": {
        "messageId": "msg-001",
        "role": "ROLE_USER",
        "parts": [{ "text": "Summarise this week'"'"'s open incidents and draft a status update." }]
      },
      "configuration": { "returnImmediately": true }
    }
  }'

The result is { "task": ... } (abridged):

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "task": {
      "id": "TASK_ID",
      "contextId": "CONTEXT_ID",
      "status": { "state": "TASK_STATE_WORKING", "timestamp": "2026-09-23T09:14:02.511Z" },
      "metadata": { "renly": { "agentId": "AGENT_ID", "conversationUrl": "https://renly.ai/chat?session=CONTEXT_ID" } }
    }
  }
}
  • Parts accepted: text, data (any JSON, passed to the agent as a JSON block) and url. Inline file bytes (raw) are refused with a content-type error. Up to 100,000 characters per message.
  • Blocking by default. Without returnImmediately, the call waits for the task to finish or pause, for up to about 200 seconds. If it is still running then, you get the task back in TASK_STATE_WORKING with a status message telling you to poll or subscribe. The run carries on either way.
  • Output. The answer arrives as an artifact named response with mediaType: "text/markdown". If you set acceptedOutputModes, include text/markdown or text/plain.
  • Continuing. To follow up after a task has finished, send a new message with the same contextId. Only contexts that were started over A2A by the same key owner can be continued; ordinary Renly chats cannot be read or extended this way.

GetTask

curl -s https://renly.ai/api/a2a \
  -H "Authorization: Bearer $RENLY_KEY" \
  -H "Content-Type: application/json" \
  -H "A2A-Version: 1.0" \
  -d '{"jsonrpc":"2.0","id":2,"method":"GetTask","params":{"id":"TASK_ID","historyLength":0}}'

Here the result is the task itself. A task waiting on a person looks like this (abridged):

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "id": "TASK_ID",
    "contextId": "CONTEXT_ID",
    "status": {
      "state": "TASK_STATE_INPUT_REQUIRED",
      "timestamp": "2026-09-23T09:15:40.020Z",
      "message": {
        "role": "ROLE_AGENT",
        "parts": [{ "text": "Ops agent is waiting for a person to approve this in Renly before it continues:\n- Post the status update to #incidents" }]
      }
    },
    "metadata": { "renly": { "agentId": "AGENT_ID", "conversationUrl": "https://renly.ai/chat?session=CONTEXT_ID" } }
  }
}

Set historyLength to include recent messages from the conversation (default 20, maximum 100; 0 for none).

The approval pause

This is the part most callers need to design for.

  1. The agent proposes an action that needs approval, such as sending a message or updating a record. The task moves to TASK_STATE_INPUT_REQUIRED, and the status message lists what is waiting.
  2. A person approves or declines on the approval card in the linked Renly conversation. No A2A method can approve, and sending another message does not approve anything.
  3. If they approve and the agent finishes, the task becomes TASK_STATE_COMPLETED with the answer in its artifact. If the agent then needs a second approval, the task stays in INPUT_REQUIRED with an updated message.
  4. If they decline, the task ends TASK_STATE_REJECTED.
  5. If you no longer want the result, call CancelTask. Any approval still waiting on the task is withdrawn, so a later click in Renly cannot run an action you abandoned.

Treat INPUT_REQUIRED as normal, not as an error. Poll with GetTask or keep a SubscribeToTask stream open, and tell your own user that a person in Renly needs to act.

All methods

MethodWhat it does
SendMessageStart a task (or continue a context) and return the task.
SendStreamingMessageSame, as Server-Sent Events: a task event, artifactUpdate events as text arrives, and a final statusUpdate.
GetTaskCurrent state, artifacts and optional history of one task.
ListTasksYour tasks, most recently updated first. Filters: contextId, status, statusTimestampAfter; paging with pageSize (1 to 100) and pageToken. With a key, only that key's tasks are listed.
CancelTaskCancel a task that has not finished, and withdraw any approval still waiting on it.
SubscribeToTaskStream status updates for an unfinished task until it ends (up to 30 minutes per stream).
GetExtendedAgentCardThe card for your key: one interface and one skill per agent you can call.

For convenience, GET /api/a2a/agent-card returns the same extended card, and GET /api/a2a/agents/AGENT_ID returns one agent's card. Both need the key.

Task states

StateMeaning in Renly
TASK_STATE_WORKINGThe agent is running.
TASK_STATE_INPUT_REQUIREDWaiting for a person to approve an action in Renly.
TASK_STATE_COMPLETEDFinished; the answer is in the response artifact.
TASK_STATE_REJECTEDA person declined the waiting action, or the conversation was still busy with an earlier message.
TASK_STATE_FAILEDThe agent could not complete it. A run interrupted by a service restart is marked failed; send the message again.
TASK_STATE_CANCELEDCancelled by the caller.

Versions and errors

Send A2A-Version: 1.0 on every request. If you leave it out but use the 1.0 method names, the request is accepted. Version 0.3 method names such as message/send are answered with a version error rather than "method not found". Batch requests are not supported.

CodeMeaning
-32600Invalid request (not JSON-RPC 2.0, or a batch)
-32601Method not found
-32602Invalid params, for example no tenant, an unknown agent, or a bad contextId
-32603Internal error
-32001Task not found
-32002Task cannot be cancelled in its current state
-32003Push notifications are not supported
-32004Unsupported operation, for example continuing a finished task or subscribing to one
-32005Content type not supported (inline file bytes, or no text output mode accepted)
-32009A2A version not supported

Authentication problems come back as HTTP errors before JSON-RPC: 401 for a missing, unknown, revoked or expired key, and 403 for a key without the a2a scope or whose owner has left the organization. Each key is limited to 60 requests a minute.