> ## 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.

# SendStreamingMessage

> Stream an A2A agent run as Server-Sent Events, with the four stream event shapes and how streamed content arrives as artifact updates

Send a message and stream the run as Server-Sent Events.

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

Same endpoint and same params as
[`SendMessage`](/docs/reference/a2a/message-send) — only the method name changes.
The response is `text/event-stream` instead of a JSON body.

## Request

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "SendStreamingMessage",
  "params": {
    "message": {
      "messageId": "0198f0c2-1a3d-7c41-9b2e-8f5a6d3c1e07",
      "role": "ROLE_USER",
      "parts": [{ "text": "Summarize this week's escalations." }]
    }
  }
}
```

## Response stream

Each SSE frame carries **one complete JSON-RPC response envelope** in its
`data:` line. Frames have no `event:` name — read `data:` and parse it.

```
data: {"jsonrpc":"2.0","id":1,"result":{"statusUpdate":{"taskId":"0198f0c2-2b4e-7d52-ac3f-9061e4d2f118","contextId":"0198f0c2-0b11-7a2f-8c9d-4e1b2a7f6c35","status":{"state":"TASK_STATE_WORKING"}}}}

data: {"jsonrpc":"2.0","id":1,"result":{"artifactUpdate":{"taskId":"0198f0c2-2b4e-7d52-ac3f-9061e4d2f118","contextId":"0198f0c2-0b11-7a2f-8c9d-4e1b2a7f6c35","artifact":{"artifactId":"...","parts":[{"text":"Three escalations "}]},"append":true,"lastChunk":false}}}

data: {"jsonrpc":"2.0","id":1,"result":{"statusUpdate":{"taskId":"0198f0c2-2b4e-7d52-ac3f-9061e4d2f118","contextId":"0198f0c2-0b11-7a2f-8c9d-4e1b2a7f6c35","status":{"state":"TASK_STATE_COMPLETED"}}}}
```

Every frame's `id` echoes your request id.

## The four event shapes

`result` is a field-presence union with exactly one key.

| Key              | Payload                                                  | Meaning                           |
| ---------------- | -------------------------------------------------------- | --------------------------------- |
| `task`           | A full `Task`                                            | A task snapshot                   |
| `message`        | A `Message`                                              | A complete message from the agent |
| `statusUpdate`   | `taskId`, `contextId`, `status`                          | A lifecycle transition            |
| `artifactUpdate` | `taskId`, `contextId`, `artifact`, `append`, `lastChunk` | Generated content                 |

<Warning>
  **Streamed content arrives as `artifactUpdate`, not as tokens.** There is no
  `token` event and no `complete` event. Accumulate `artifactUpdate` frames
  whose `append` is `true`, and treat `lastChunk: true` as the end of that
  artifact.
</Warning>

Errors mid-stream arrive as an ordinary frame carrying a JSON-RPC `error`
instead of a `result`.

## Reading the stream

```typescript theme={null}
const resp = await fetch("https://agent-my-agent.noorle.com/", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "SendStreamingMessage",
    params: { message: { messageId: crypto.randomUUID(), role: "ROLE_USER", parts: [{ text }] } },
  }),
});

const reader = resp.body!.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += value;

  // SSE frames are separated by a blank line.
  const frames = buffer.split("\n\n");
  buffer = frames.pop() ?? "";

  for (const frame of frames) {
    const line = frame.split("\n").find((l) => l.startsWith("data:"));
    if (!line) continue;
    const envelope = JSON.parse(line.slice(5).trim());

    if (envelope.error) { /* handle and stop */ continue; }

    const r = envelope.result;
    if (r.artifactUpdate) { /* append r.artifactUpdate.artifact.parts */ }
    else if (r.statusUpdate) { /* r.statusUpdate.status.state */ }
    else if (r.message) { /* a complete agent message */ }
    else if (r.task) { /* a task snapshot */ }
  }
}
```

## Terminal states

The stream ends when the task reaches a terminal state. Task states are
`TASK_STATE_*` strings — see [Tasks](/docs/reference/a2a/tasks) for the full set and
which four are terminal.

`TASK_STATE_INPUT_REQUIRED` is **not** terminal. It means the run is parked
waiting on a human — most often an approval gate. Resume with a follow-up
`SendMessage`; see [A2A overview](/docs/reference/a2a/overview).

## Reconnecting

If the connection drops mid-run, use
[`SubscribeToTask`](/docs/reference/a2a/tasks) rather than resending the message. It
replays the current snapshot and then tails live updates.

## Related

* [SendMessage](/docs/reference/a2a/message-send)
* [Tasks](/docs/reference/a2a/tasks)
* [Push notifications](/docs/reference/a2a/push-notifications)
* [A2A overview](/docs/reference/a2a/overview)
