# Стриминг (SSE)

Нативный API стримит через Server-Sent Events по **двухшаговому протоколу**: вы запускаете ход, затем подписываетесь на его устойчивый журнал событий. Разделение делает стрим возобновляемым — оборванное соединение переподключается и доигрывает с места обрыва, а один ход можно смотреть из нескольких вкладок.

**Нужен drop-in в одну строку?**

Если вам нужен только потоковый текст и вы уже используете OpenAI SDK — [OpenAI-совместимый эндпоинт](/docs/openai-compatible) стримит в одном запросе и проще. Эта страница описывает нативный протокол и полный набор его событий.

## Протокол

### Запустите ход

`POST /api/v1/sessions/stream` **не** стримит — он возвращает `202 Accepted` с идентификаторами хода, запущенного в фоне.

```bash
curl -X POST "https://api.samreshuuu.ru/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"
}
```

### Подпишитесь на журнал событий

`GET /api/v1/sessions/{session_id}/messages/{message_id}/stream` возвращает `text/event-stream` и доигрывает все события этого хода. Каждый фрейм несёт `id:` — сохраняйте последний, чтобы возобновиться.

```bash
curl -N "https://api.samreshuuu.ru/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}
```

## Поля запроса

Тело `POST /sessions/stream` принимает:

<ParamTable
    rows={[
        { name: "message", type: "string", required: true, description: "Ход пользователя. Обязателен message либо document_ids." },
        { name: "session_id", type: "string", description: "Передайте session_id, чтобы продолжить предыдущий разговор. Опустите для нового — ответ вернёт новый id." },
        { name: "agent_id", type: "string", description: "Выполнить ход под профилем конкретного агента и выданными ему коннекторами. См. Агенты." },
        { name: "document_ids", type: "array", description: "Прикрепить ранее загруженные документы к ходу по id." },
    ]}
/>

## Типы событий

Каждый фрейм — это `data:` и JSON-объект с дискриминатором `type`. Это стабильные публичные типы событий — обрабатывайте нужные и **игнорируйте любой `type`, который не распознали**; набор расширяемый.

| Тип | Значение |
| --- | --- |
| `start` | Ход начался. Несёт `session_id`. |
| `delta` | Инкремент текста ассистента — добавляйте `content` для живого рендера. |
| `tool_started` | Начался вызов инструмента. Несёт `tool_name`, `label`, `iteration`. |
| `tool_completed` | Инструмент завершился. Несёт `success`, `message`, `duration_ms`. |
| `artifact_start` | Начал стримиться файл/артефакт. Несёт `artifact_id`, `artifact_type`, `title`. |
| `artifact_delta` | Чанк содержимого артефакта (`content_chunk`, `chunk_index`). |
| `artifact_complete` | Артефакт готов. Несёт `version`, `content_type`, опционально `download_url`. |
| `artifact_update` | Ранее завершённый артефакт был изменён. |
| `complete` | **Терминальный успех.** Несёт `final_response`, счётчики токенов и `artifacts`. |
| `error` | **Терминальная ошибка.** Несёт код `error`, `message`, `retryable`, `user_message`. |
| `cancelled` | Ход остановлен. Несёт `reason` (`user_stop`, `timeout`, …) и `partial_response`, если есть. |
| `paused` | Ход приостановлен в ожидании пробуждения (форма, таймер или rate-limit). Стрим закрывается штатно — возобновите позже с теми же id. |
| `form_request` | Агенту нужен ввод. Несёт `form_kind` (`clarify` \| `integration` \| `signature` \| `approval`), `form_id` и `payload`. |

**Канонический ответ — в событии complete**

Рендерьте текст `delta` вживую для UX, но авторитетный ответ — это `complete.final_response`, а не склейка дельт. Агент может переписать ответ по ходу, поэтому дельты — промежуточный нарратив, а `final_response` равен сохранённому сообщению.

Также могут появляться транзиентные диагностические события (`thinking`, `heartbeat`, `retry_notification`, `browser_frame`) — они не входят в стабильный контракт, их можно пропускать.

## Возобновление оборванного стрима

Эндпоинт подписки идемпотентен и безопасен для нескольких вкладок. Чтобы возобновиться после обрыва, верните id последнего фрейма — заголовком `Last-Event-ID` или query-параметром `?since=` — и сервер доиграет только пропущенное.

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

## Ответ на form_request

Когда приходит `form_request`, ход приостанавливается (можно также увидеть событие `paused`). Отправьте ответ пользователя обычным `POST /sessions/stream` с **тем же `session_id`** — платформа направит его в ожидающую форму, а не начнёт новый ход, и исходный ход возобновится на своём стриме.

## Остановка хода

`POST /api/v1/sessions/{session_id}/stop` отменяет активный ход. Идемпотентен и всегда возвращает `204`; стрим затем отдаёт событие `cancelled` с `reason: "user_stop"`.

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

**Полезные ссылки**

- [OpenAI-совместимый API — стриминг в одном запросе](/docs/openai-compatible)
- [Агенты — выполнить ход под профилем агента](/docs/agents)
- [Ошибки и лимиты — коды ошибок и retry-логика](/docs/reference)
