Ai streaming

Poll a stream

GET/ai/streams/{streamId}

Returns the current accumulated content. Safe to poll. Once isComplete is true, stop polling and either inspect content here or GET /ai/chats/{ref} for the saved assistant message.

X-Api-Key<token>

Issued to you when your integration is provisioned. Determines which catalogue resources are visible and which AI tools are available.

In: header

Path Parameters

streamId*string

Response Body

application/json

application/json

curl -X GET "https://example.com/ai/streams/string"
{  "data": {    "streamId": "string",    "chatRef": 0,    "content": "string",    "isComplete": true,    "status": "streaming",    "toolStatus": "string",    "reasoning": "string",    "results": [      {        "title": "string",        "preview": "string",        "resourceId": 0,        "score": 0,        "rawScore": 0,        "matchSource": "metadata",        "hits": 0      }    ]  }}
{  "success": false,  "status": "fail",  "message": "string",  "error": {    "message": "API key is required",    "code": "NO_API_KEY"  }}

Stream chat response deltas (Server-Sent Events) GET

Server-sent-events counterpart to `GET /ai/streams/{streamId}`. Instead of returning a snapshot the caller re-polls, this **holds the connection open** and pushes the assistant's response as it is generated. The server polls the DB-backed stream (~300ms) and flushes each new chunk until the message completes. Requires the `ai` key scope and a linked service user. The `streamId` must belong to the linked user — one key can never read another key's stream (`404` otherwise). ### Usage Open this on its own connection **after** issuing the message POST on a separate connection (PHP-FPM blocks per request): 1. `POST /ai/streams/init {chatRef}` → `streamId` 2. `POST /ai/chats/{ref}/messages {message, streamId}` (on connection A) 3. `GET /ai/streams/{streamId}/events` (on connection B) — consume the event stream below Standard `text/event-stream` semantics — use any SSE client (`EventSource`, `curl -N`, an SSE library). The connection is hard-capped server-side (see **Bounds**) and closes itself on completion. ### Event types | `event:` | `data:` payload | Meaning | |----------|-----------------|---------| | `delta` | `{ "content": "<text>" }` | Text appended since the previous event — concatenate onto what you have. | | `delta` | `{ "content": "<full text>", "replace": true }` | Rare: earlier content was rewritten (e.g. a follow-ups block streamed in then got stripped). Replace your buffer with `content` rather than appending. | | `done` | `StreamDoneEvent` | Terminal. Stream completed (or was aborted); connection closes. Carries the fully-cleaned final content plus `results` / `followUpQuestions`. | | `error` | `StreamErrorEvent` | Terminal. The stream ended in an error state, the row vanished, or the connection hit its time cap. | Lines beginning `:` (e.g. `: keepalive`) are SSE comments emitted when the stream is idle to defeat proxy buffering — ignore them. ### Bounds A connection lives for exactly one chat response (seconds, not a subscription). It is hard-capped at **120s**; if the underlying stream has not completed by then the server emits a terminal `error` (`status: "timeout"`) and closes. A vanished client is detected each poll and frees the worker within one poll interval. Per-key rate limits cap concurrent streams per integration. ### Example ```text event: delta data: {"content":"Once upon a time, a publisher asked"} event: delta data: {"content":" for server-sent events."} : keepalive event: done data: {"status":"complete","content":"Once upon a time, a publisher asked for server-sent events.","reasoning":null,"results":[],"followUpQuestions":[]} ```

Initialise a stream POST

Creates a stream bound to a chat. Pass the returned `streamId` on `POST /ai/chats/{ref}/messages` and poll `GET /ai/streams/{streamId}` from a separate connection. Note: the message POST is synchronous and won't release until the AI finishes. Polling **must** happen on a different connection (a separate thread / async task in your client).