> ## Documentation Index
> Fetch the complete documentation index at: https://noorle.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# A2A Protocol

> A2A 1.0 over JSON-RPC on Noorle agent gateways: the eleven methods, endpoints, streaming, extensions, and authentication

Every agent you publish gets an agent gateway — an A2A 1.0 endpoint another
system can send messages to.

```
https://agent-{handle}.noorle.com
```

The host is derived from the agent's handle.

## Endpoints

| Method | Path                           | Auth       | Purpose                                                 |
| ------ | ------------------------------ | ---------- | ------------------------------------------------------- |
| `GET`  | `/.well-known/agent-card.json` | **Public** | [The agent card](/docs/reference/a2a/agent-card)             |
| `POST` | `/`                            | Bearer     | Every A2A JSON-RPC method, including the streaming ones |
| `POST` | `/stream`                      | Bearer     | Legacy alias for `/`                                    |
| `POST` | `/agui`, `/ag-ui`              | Bearer     | AG-UI `RunAgentInput` to `text/event-stream`            |

**Every A2A method is a `POST` to `/`.** The method name lives in the JSON-RPC
envelope, not the path. There are no REST-shaped routes — no `GET /tasks/{id}`,
no `DELETE /tasks/{id}`.

<Warning>
  **There is no WebSocket endpoint.** Neither this gateway nor the MCP gateway
  exposes one. All streaming is Server-Sent Events on a POST response.
</Warning>

## Methods

The gateway dispatches exactly eleven method names and returns
`method not found` (-32601) for anything else.

| Method                             | Streaming | Purpose                                                            |
| ---------------------------------- | --------- | ------------------------------------------------------------------ |
| `SendMessage`                      | —         | [Send a message, wait for the result](/docs/reference/a2a/message-send) |
| `SendStreamingMessage`             | **SSE**   | [Send a message, stream the result](/docs/reference/a2a/message-stream) |
| `GetTask`                          | —         | [Fetch a task snapshot](/docs/reference/a2a/tasks)                      |
| `ListTasks`                        | —         | —                                                                  |
| `CancelTask`                       | —         | [Cancel a running task](/docs/reference/a2a/tasks)                      |
| `SubscribeToTask`                  | **SSE**   | [Replay then tail a task](/docs/reference/a2a/tasks)                    |
| `CreateTaskPushNotificationConfig` | —         | [Register a push target](/docs/reference/a2a/push-notifications)        |
| `GetTaskPushNotificationConfig`    | —         | [Read a push config](/docs/reference/a2a/push-notifications)            |
| `ListTaskPushNotificationConfigs`  | —         | [List push configs](/docs/reference/a2a/push-notifications)             |
| `DeleteTaskPushNotificationConfig` | —         | [Remove a push config](/docs/reference/a2a/push-notifications)          |
| `GetExtendedAgentCard`             | —         | Returns the same card the well-known endpoint serves               |

<Warning>
  **Method names are A2A 1.0 PascalCase.** The 0.x slash names —
  `message/send`, `message/stream`, `tasks/get`, `tasks/resubscribe`,
  `tasks/pushNotificationConfig/*` — are **not accepted** and return -32601.
  There are no aliases.
</Warning>

`SendStreamingMessage` and `SubscribeToTask` return `text/event-stream`.
Everything else returns a JSON-RPC response body.

## Errors arrive with HTTP 200

Every JSON-RPC error is returned with **HTTP 200** and the error in the
envelope. The HTTP status is not the failure signal on this surface.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": { "code": -32601, "message": "Method not found" }
}
```

## Authentication

The gateway takes one bearer credential in two kinds, disambiguated by prefix:
an `ak-` API key belonging to a **service principal**, or a JWT scoped to this
agent. A user's API key is rejected with 403.

```bash theme={null}
curl -X POST https://agent-my-agent.noorle.com/ \
  -H "Authorization: Bearer eyJhbGc..." \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"GetTask","params":{"id":"..."}}'
```

The agent card is public by protocol convention — it is the contract that tells
a caller what credential to attach.

A run authorizes as the **caller**, using the caller's resolved role and grants.
There is no elevation.

<Card title="Authentication" icon="key" href="/docs/reference/authentication">
  Credential kinds, JWT admission rules, and the delegation chain.
</Card>

<Note>
  **The agent gateway has no rate limiting.**
</Note>

## Contexts and threads

The A2A `contextId` **is the Noorle thread id**. Echo it on a follow-up turn to
continue the same conversation; omit it and a fresh thread is created.

A client-supplied `taskId` is honored if present, otherwise the server mints
one. Task creation is atomic — reusing a task id fails rather than clobbering
the existing task.

Every stored key is scoped by account and agent, derived from the authenticated
context. A `tenant` field on the wire is never trusted.

## Extensions

The card declares two Noorle extensions, both optional:

| URI                                           | Purpose                                 |
| --------------------------------------------- | --------------------------------------- |
| `https://a2a.noorle.dev/ext/a2ui/v1`          | Generative UI surface artifacts         |
| `https://a2a.noorle.dev/ext/approval-gate/v1` | Approval-gate `input-required` messages |

Negotiate by sending `A2A-Extensions` with a comma-separated list of URIs.
The legacy header `X-A2A-Extensions` is also read; the response always echoes
the canonical `A2A-Extensions` name. Unknown URIs are ignored.

Messages and artifacts carrying an extension payload are tagged with the
extension URI **only if you negotiated it**.

## Approval gates

When an agent's autonomy policy pauses a call, the task moves to a non-terminal
`TASK_STATE_INPUT_REQUIRED` — the HTTP or SSE request is not held open.

Resume by sending a follow-up `SendMessage` whose parts carry a
`noorle.gate_decision` data part. Both `taskId` and `contextId` are required;
both are server-minted and echoed back to you.

Only **user** callers may resolve a gate over A2A.

A rejected decision returns a `noorle.gate_decision_rejected` message on the
blocking path, or a single non-final `INPUT_REQUIRED` update on the streaming
path. The task stays parked either way.

## Wire shapes worth knowing up front

* **Task states are `TASK_STATE_*` screaming-snake strings** on the wire, not
  lowercase words. See [Tasks](/docs/reference/a2a/tasks).
* **Roles are `ROLE_USER` and `ROLE_AGENT`.**
* **A message part has no `file` variant.** The union is `text`, `raw`
  (base64), `url`, and `data`. File attachments lower to `raw` or `url` with
  `filename` and `mediaType` alongside.
* **Streamed content rides artifact updates**, not messages.

## Next steps

* [Agent Card](/docs/reference/a2a/agent-card)
* [SendMessage](/docs/reference/a2a/message-send)
* [SendStreamingMessage](/docs/reference/a2a/message-stream)
* [Tasks](/docs/reference/a2a/tasks)
* [Push Notifications](/docs/reference/a2a/push-notifications)
