# Streaming (SSE)

The native API streams over Server-Sent Events with a **two-step protocol**: you spawn a turn, then subscribe to its durable event log. The split makes the stream resumable — a dropped connection reconnects and replays from where it left off, and the same turn can be watched from multiple tabs.

**Looking for a one-line drop-in?**

If you only need plain streamed text and already use the OpenAI SDK, the [OpenAI-Compatible endpoint](/docs/openai-compatible) streams in a single request and is the simpler path. This page documents the native protocol and its full event set.

## The protocol

### Spawn the turn

`POST /api/v1/sessions/stream` does **not** stream — it returns `202 Accepted` with the identifiers of the turn that was spawned in the background.

```bash
curl -X POST "https://api.samreshuuu.com/api/v1/sessions/stream" \
  -H "Authorization: Bearer sk-org-your_api_key" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Summarize the key points of this contract" }'
```

```json
{
  "session_id": "ses_abc123",
  "message_id": "msg_def456",
  "kind": "new_turn"
}
```

### Subscribe to the event log

`GET /api/v1/sessions/{session_id}/messages/{message_id}/stream` returns `text/event-stream` and replays every event for that turn. Each frame carries an `id:` — keep the last one to resume.

```bash
curl -N "https://api.samreshuuu.com/api/v1/sessions/ses_abc123/messages/msg_def456/stream" \
  -H "Authorization: Bearer sk-org-your_api_key"
```

```json
id: 1709-0
data: {"type": "start", "session_id": "ses_abc123"}

id: 1710-0
data: {"type": "delta", "content": "Here are the key points of the contract:\n\n"}

id: 1711-0
data: {"type": "tool_started", "tool_name": "web_search", "label": "Searching the web", "iteration": 1}

id: 1715-0
data: {"type": "complete", "session_id": "ses_abc123", "final_response": "Here are the key points...", "total_input_tokens": 150, "total_output_tokens": 89}
```

## Request fields

The `POST /sessions/stream` body accepts:

<ParamTable
    rows={[
        { name: "message", type: "string", required: true, description: "The user turn. Either message or document_ids is required." },
        { name: "session_id", type: "string", description: "Reuse a session_id to continue a prior conversation. Omit to start fresh — the response returns the new id." },
        { name: "agent_id", type: "string", description: "Run the turn under a specific agent profile and its granted connectors. See Agents." },
        { name: "document_ids", type: "array", description: "Attach previously uploaded documents to the turn by id." },
    ]}
/>

## Event types

Every frame is `data:` followed by a JSON object with a `type` discriminator. These are the stable, public event types — handle the ones you need and **ignore any `type` you don't recognize**; the set is additive.

| Type | Meaning |
| --- | --- |
| `start` | Turn began. Carries `session_id`. |
| `delta` | Incremental assistant text — append `content` for live rendering. |
| `tool_started` | A tool call began. Carries `tool_name`, `label`, `iteration`. |
| `tool_completed` | Tool finished. Carries `success`, `message`, `duration_ms`. |
| `artifact_start` | A file/artifact began streaming. Carries `artifact_id`, `artifact_type`, `title`. |
| `artifact_delta` | A chunk of artifact content (`content_chunk`, `chunk_index`). |
| `artifact_complete` | Artifact finished. Carries `version`, `content_type`, optional `download_url`. |
| `artifact_update` | A previously completed artifact was revised. |
| `complete` | **Terminal success.** Carries `final_response`, token counts, and `artifacts`. |
| `error` | **Terminal failure.** Carries `error` code, `message`, `retryable`, `user_message`. |
| `cancelled` | Turn stopped. Carries `reason` (`user_stop`, `timeout`, …) and any `partial_response`. |
| `paused` | Turn suspended awaiting a wake (form, timer, or rate-limit). The stream closes cleanly — resume later with the same ids. |
| `form_request` | The agent needs input. Carries `form_kind` (`clarify` \| `integration` \| `signature` \| `approval`), `form_id`, and a `payload`. |

**The canonical answer is on the complete event**

Render `delta` text live for UX, but the authoritative answer is `complete.final_response` — not the concatenation of deltas. The agent may revise mid-stream, so deltas are interstitial narrative, while `final_response` equals the persisted message.

Transient diagnostic events (`thinking`, `heartbeat`, `retry_notification`, `browser_frame`) may also appear; they are not part of the stable contract and are safe to skip.

## Resuming a dropped stream

The subscribe endpoint is idempotent and multi-tab safe. To resume after a disconnect, send the last frame id back — either as the `Last-Event-ID` header or a `?since=` query parameter — and the server replays only what you missed.

```bash
curl -N "https://api.samreshuuu.com/api/v1/sessions/ses_abc123/messages/msg_def456/stream?since=1711-0" \
  -H "Authorization: Bearer sk-org-your_api_key"
```

## Answering a form_request

When a `form_request` arrives, the turn pauses (you may also see a `paused` event). Send the user's answer back as a normal `POST /sessions/stream` with the **same `session_id`** — the platform routes it to the waiting form instead of starting a new turn, and the original turn resumes on its existing stream.

## Stopping a turn

`POST /api/v1/sessions/{session_id}/stop` cancels the active turn. It is idempotent and always returns `204`; the stream then emits a `cancelled` event with `reason: "user_stop"`.

```bash
curl -X POST "https://api.samreshuuu.com/api/v1/sessions/ses_abc123/stop" \
  -H "Authorization: Bearer sk-org-your_api_key"
```

**Quick Links**

- [OpenAI-Compatible API — single-request streaming](/docs/openai-compatible)
- [Agents — run a turn under an agent profile](/docs/agents)
- [Errors & Rate Limits — error codes and retry logic](/docs/reference)
