We're heading to KubeCon Mumbai·June 18–19

Let's meet

Webhook (A2A)

Invoke agents over HTTP using the A2A protocol

5 min read

Webhook (A2A) lets external systems invoke an agent by POSTing to a per-agent URL. The endpoint follows the A2A (Agent-to-Agent) 1.0 protocol for interoperability with other A2A-compatible callers.

Endpoints

MethodPathPurpose
GET/v2/a2a/{agentId}/.well-known/agent-card.jsonA2A 1.0 AgentCard for capability discovery
POST/v2/a2a/{agentId}JSON-RPC entry point for SendMessage and GetTask

The AgentCard advertises the agent's name, description, and a single skill derived from the agent. A2A clients fetch it first to discover what the agent can do, then send messages to the POST endpoint.

SDKs

SDKA2A 1.0 supportWorks with /v2/a2a
a2a-pythonYesYes — use directly
a2a-js (TypeScript)Not yet — tracked in a2a-js#321No — speak JSON-RPC directly, or translate role / state enums between 0.3 and 1.0

Other languages can call the JSON-RPC endpoint directly using the request shapes below.

Authentication

Both endpoints require an Archestra token in the Authorization header:

Authorization: Bearer <platform_token>

A personal token from Settings > Your Account, a team token from Settings > Teams, or the organization token from Settings > Organization all work, as long as the token has access to the target agent.

SendMessage

JSON-RPC method SendMessage runs a message against the agent.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "SendMessage",
  "params": {
    "message": {
      "messageId": "11111111-1111-1111-1111-111111111111",
      "role": "ROLE_USER",
      "parts": [{ "text": "Summarize the last 5 PRs in repo X." }]
    }
  }
}

Field notes:

  • messageId — required, must be unique per message (UUIDs recommended).
  • roleROLE_USER for caller, ROLE_AGENT for the agent's reply.
  • parts[].text — message body.
  • contextId / taskId — omit on the first message; copy from the response for follow-up turns.

The response is one of two shapes inside result:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "message": {
      "messageId": "...",
      "role": "ROLE_AGENT",
      "contextId": "327a5306-c7dc-4e0c-ba2f-107da6c2548b",
      "parts": [{ "text": "Here is the summary..." }]
    }
  }
}

If the agent needs human approval before running a tool, result contains a task with status.state = "TASK_STATE_INPUT_REQUIRED" and metadata.approvalRequests. See Approvals.

Multi-turn conversations

To keep messages in the same conversation, copy contextId from the first response into every subsequent request:

# Turn 1
curl -X POST https://archestra.example.com/v2/a2a/<agentId> \
  -H "Authorization: Bearer <platform_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "SendMessage",
    "params": {
      "message": {
        "messageId": "11111111-1111-1111-1111-111111111111",
        "role": "ROLE_USER",
        "parts": [{ "text": "hi, my name is victor" }]
      }
    }
  }'
# → result.message.contextId = "327a5306-..."

# Turn 2 — reuse contextId
curl -X POST https://archestra.example.com/v2/a2a/<agentId> \
  -H "Authorization: Bearer <platform_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "SendMessage",
    "params": {
      "message": {
        "messageId": "22222222-2222-2222-2222-222222222222",
        "role": "ROLE_USER",
        "contextId": "327a5306-c7dc-4e0c-ba2f-107da6c2548b",
        "parts": [{ "text": "do you know who i am?" }]
      }
    }
  }'

contextId is generated by Archestra on the first message. Clients cannot supply their own.

X-Archestra-Session-Id and Mcp-Session-Id do not group conversations — they are observability-only headers. Use contextId to continue a conversation.

Approvals

When an agent's tool call hits a tool invocation policy requiring approval, the response is a task, not a message:

{
  "result": {
    "task": {
      "id": "task-...",
      "contextId": "ctx-...",
      "status": { "state": "TASK_STATE_INPUT_REQUIRED" },
      "metadata": {
        "approvalRequests": [
          { "approvalId": "appr-...", "toolName": "send_email", "approved": false, "resolved": false }
        ]
      }
    }
  }
}

To approve (or reject), send a follow-up SendMessage with taskId, contextId, and decisions in metadata.taskOps:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "SendMessage",
  "params": {
    "message": {
      "messageId": "33333333-3333-3333-3333-333333333333",
      "role": "ROLE_USER",
      "taskId": "task-...",
      "contextId": "ctx-...",
      "parts": [],
      "metadata": {
        "taskOps": {
          "approvalDecisions": [{ "approvalId": "appr-...", "approved": true }]
        }
      }
    }
  }
}

Approvals also work through Slack/Teams ChatOps. The same flow handles multi-request and multi-turn approvals.

GetTask

Use GetTask to fetch the current state of a task (useful while polling an approval task):

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "GetTask",
  "params": { "id": "task-..." }
}

Pass-through payload (v1 only)

The legacy POST /v1/a2a/{agentId} endpoint accepts any non-A2A JSON body. The body is stringified and passed to the agent as the user message — useful for tools like Zapier that just want to fire an event at an agent:

{
  "event": "issue_opened",
  "title": "Login button broken on Safari",
  "url": "https://github.com/acme/app/issues/1421"
}

v1 is single-turn — every call is a fresh conversation. For multi-turn use v2 with a SendMessage envelope.

Observability

Pass a session ID to group all LLM and MCP tool calls in Observability:

X-Archestra-Session-Id: my-session-123

Without it, Archestra generates one per request. The header is independent of contextId — it tags traces only.

Configuration

A2A uses the same LLM configuration as Chat. See Deployment - Environment Variables for the full list of ARCHESTRA_CHAT_* variables.