Ai images

Stream a chat image

GET/ai/images

Returns image bytes for an image saved by the chat pipeline (either attached by the caller or generated by image_generation).

Two auth modes

  • Signed URL (preferred for browsers): pass path, exp, sig query params as returned in the images[i].url field of a chat response. The signature is validated and bytes are served without an api-key. URLs expire after 1 hour.
  • API key (server-to-server): send X-Api-Key and path only. The path is checked against the api-key's scope; you can only fetch images that belong to chats the api-key would otherwise be able to read.

Inline image refs in chatResponse HTML are auto-rewritten to signed-URL form, so customers rendering the response HTML directly get working <img src> tags out of the box.

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

Query Parameters

path*string

Opaque path identifier from images[i].path on a chat response. Pass it back verbatim — don't construct or modify it.

exp?integer

Unix expiry timestamp. Required for signed-URL auth.

sig?string

URL signature. Required for signed-URL auth.

Response Body

application/json

application/json

application/json

curl -X GET "https://example.com/ai/images?path=%2Fchat_images%2F1024%2Fgpt_xxx.png"
"string"
{  "success": false,  "status": "fail",  "message": "string",  "error": {    "message": "API key is required",    "code": "NO_API_KEY"  }}

{  "success": false,  "status": "fail",  "message": "API key is required",  "error": {    "code": "NO_API_KEY",    "message": "API key is required"  }}

Empty
{  "success": false,  "status": "fail",  "message": "string",  "error": {    "message": "API key is required",    "code": "NO_API_KEY"  }}

Research Amazon book keywords POST

Researches Amazon search-volume and competition metrics for a set of seed terms (comparable authors, series, distinctive titles, seasonal search phrases). Direct REST equivalent of the chat `book_keyword_research` tool. Requires the `ai` key scope and a linked service user. When `marketplace` is omitted it defaults to the linked user's usergroup market (never a hardcoded US).

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":[]} ```