Octri

Streaming

Some responses arrive over time rather than all at once: a token stream from a model, a log tail, a long export. A streaming method hands the caller each event as it arrives, in whatever form is natural for their language.

An operation whose success response declares text/event-stream streams automatically. For the other formats, configure it per endpoint.

What callers write

These are from generated SDKs, for an operation GET /events/stream tagged events.

typescript
for await (const event of client.events.stream()) {
  console.log(event.id);
}

Returns an AsyncIterable.

Formats

FormatWire shapeUse it when
Server-sent eventstext/event-stream. Each event prefixed data: and ended by a blank lineModel output and live feeds. It's the most common choice
NDJSONOne complete JSON object per line, such as application/x-ndjsonExports and logs, with no event framing to parse
ChunkedRaw bytes or textThe response has no structure of its own

For server-sent events, the client handles the framing for you: it joins multi-line data: fields, ignores keep-alive comments, and treats [DONE] as the end of the stream.

Setting it up

In SDK Studio, select the endpoint and open its streaming settings. Each is saved on that endpoint, under streaming.

Transport formatchoicedefault: detected

The wire format your endpoint uses.

ValueStudio labelMeaning
sseSSEServer-sent events: text/event-stream, one event per message
ndjsonNDJSONNewline-delimited JSON: one JSON object per line
chunkedChunkedRaw chunked transfer, surfacing bytes as they arrive

streaming.format

Event fieldfielddefault: data

The field surfaced from each chunk.

streaming.eventField

For events shaped like this, leave Event field as data, and each iteration yields the parsed object:

text
data: {"id": "evt_1", "type": "user.created"}

data: {"id": "evt_2", "type": "user.updated"}

Stream key

Some endpoints answer with plain JSON or a stream, depending on a field in the request body. The SDK generates two methods for these: a normal one and a stream one. The stream method sets the field for the caller and leaves it out of its arguments.

Stream key is set to Automatic by default, which looks for a boolean field named stream, event, streaming or eventMode. If your API uses another name, such as sse, pick that field from the list.

If the field is text rather than a boolean, also set Stream value. For a response_mode field that takes streaming or blocking, the stream key is response_mode and the stream value is streaming. Callers of the normal method can still pass blocking.

Declaring it in your spec

Declaring the media type is usually all you need:

yaml
responses:
  "200":
    description: A stream of events
    content:
      text/event-stream:
        schema:
          $ref: "#/components/schemas/Event"

Give the schema a $ref to your event, so callers get typed events rather than untyped data.

An operation can also offer both forms. If it declares JSON and text/event-stream, callers get an ordinary method and a separate streaming companion.

Stopping early

Stop iterating as soon as you don't need more events, so the connection can close promptly. For cancellation from elsewhere, pass a signal:

typescript
const controller = new AbortController();

for await (const event of client.events.stream({ signal: controller.signal })) {
  if (event.type === "done") break;
}

Streaming methods take fewer options than ordinary ones. In TypeScript they accept headers and signal, not per-call retry or timeout settings.

Failures

Retries apply to establishing the request. Once events have reached the caller, replaying the request would duplicate them, so a stream that breaks surfaces its error instead.

Design streams so callers can resume

Put an offset, a sequence number or a cursor in each event, and let the endpoint accept it. Otherwise a dropped connection means starting again from the beginning.

A record that looks like JSON but doesn't parse raises SdkStreamParseError, or that language's equivalent.

Timeouts

Don't set a short timeout on a streamed endpoint. Give it its own timeout that fits the stream, rather than inheriting one meant for ordinary calls.

Don't stream everything

Streaming costs the caller a loop and gives up a plain return value. Use it when the first byte matters, or when the response has no natural end. A 40 ms JSON response should stay a JSON response.