How AI is applied across API Evangelist and APIs.io. Read my AI disclosure →
API Evangelist API Evangelist
Discovery
Learnings
Guidance
Toolbox
Alignment
API Evangelist LLC

Event streaming in OpenAPI 3.2: What changed and why it matters

calendar_today October 6, 2025 person Zaid Daba'een domain developerhub

Event streaming in OpenAPI 3.2: What changed and why it matters<p>OpenAPI 3.2 brings native, first‑class ways to describe APIs that send data as a sequence of events instead of a single, monolithic payload. This is a big deal for real‑time apps—LLMs, analytics feeds, chat, logs, and anything that benefits from progressive rendering or lower perceived latency. At a high level, OpenAPI 3.2 formalizes “sequential” media types and lets you specify the schema of each item in the stream using a new itemSchema on a response media type. That means your documentation can be explicit about the shape of each event, not just the overall connection.</p><ul><li>New capability: Use itemSchema under content to define the structure of each streamed event.</li></ul><p>Supported sequential media types:</p><ul><li>SSE: text/event-stream</li><li>JSON Lines: application/jsonl</li><li>JSON Sequences: application/json-seq</li><li>Multipart Mixed: multipart/mixed</li></ul><p>This unlocks consistent tooling, clearer client expectations, and better validation for streaming APIs.</p><h3 id="a-quick-primer-how-streaming-differs-from-normal-responses">A quick primer: how streaming differs from “normal” responses</h3><ul><li>Normal response: One payload, one schema.</li><li>Streaming response: Many items over time; each item conforms to the itemSchema. The transport stays open while the server emits items; the client processes incrementally.</li></ul><p>Common patterns you’ll see:</p><ul><li>SSE for text/token streams in browsers</li><li>JSONL for structured event logs and incremental model outputs</li><li>Multipart for mixed binary/text chunks (e.g., speech + text)</li><li>Sentinel events like [DONE] to cleanly signal the end of a stream</li></ul><h3 id="example-describing-an-llm-s-streaming-api-sse-">Example: describing an LLM’s streaming API (SSE)</h3><p>Here’s a minimal but realistic OpenAPI 3.2 spec for a text‑generation endpoint that streams tokens via Server‑Sent Events. The server emits events as they become available; clients render tokens progressively.</p><pre>openapi: 3.2.0 info: title: LLM Streaming API version: 1.0.0 paths: /generate: post: summary: Stream generated text from the LLM description: | Streams model output incrementally using Server-Sent Events (SSE). Each event contains a token chunk; a sentinel signals the end. requestBody: required: true content: application/json: schema: type: object required: [model, prompt] properties: model: type: string description: Model identifier (e.g., "gpt-4o-mini") prompt: type: string description: The input prompt for text generation max_tokens: type: integer minimum: 1 default: 256 temperature: type: number minimum: 0 maximum: 2 default: 0.7 responses: "200": description: Stream of generation events via SSE headers: Content-Type: schema: type: string enum: ["text/event-stream"] content: text/event-stream: itemSchema: oneOf: - type: object required: [event, data] properties: event: type: string enum: ["token"] description: Event type data: type: object required: [text, index] properties: text: type: string description: Token or text chunk index: type: integer minimum: 0 description: Incrementing token index logprobs: type: number nullable: true description: Optional per-token log prob finish_reason: type: string nullable: true enum: ["stop", "length", "content_filter", null] - type: object required: [event, data] properties: event: type: string enum: ["summary"] data: type: object properties: usage: type: object properties: prompt_tokens: { type: integer, minimum: 0 } completion_tokens: { type: integer, minimum: 0 } total_tokens: { type: integer, minimum: 0 } model: type: string - type: object required: [event, data] properties: event: type: string enum: ["done"] data: type: string enum: ["[DONE]"] "400": description: Invalid request content: application/json: schema: type: object required: [error] properties: error: type: string "429": description: Rate limited "500": description: Server error</pre><p>Notes:</p><ul><li>text/event-stream matches the SSE transport browsers understand.</li><li>itemSchema with oneOf captures normal token events, an optional final summary, and the explicit end‑of‑stream sentinel.</li><li>Errors follow regular non‑streaming JSON shapes with standard HTTP codes.</li></ul><h3 id="variant-json-lines-jsonl-streaming-for-sdks-and-backend-clients">Variant: JSON Lines (JSONL) streaming for SDKs and backend clients</h3><p>If your clients prefer framed JSON instead of SSE, you can offer application/jsonl with the same itemSchema. Each line is one JSON object.</p><pre>responses: "200": description: Stream of generation events via JSON Lines content: application/jsonl: itemSchema: oneOf: - type: object required: [type, token] properties: type: type: string enum: ["token"] token: type: object required: [text, index] properties: text: { type: string } index: { type: integer, minimum: 0 } - type: object required: [type, data] properties: type: type: string enum: ["done"] data: type: string enum: ["[DONE]"]</pre><h3 id="designing-a-great-streaming-contract">Designing a great streaming contract</h3><ul><li>Be explicit about termination: Include a clear sentinel (e.g., event: “done” with data: “[DONE]”) so clients can stop reading deterministically.</li><li>Separate event kinds: Use oneOf to model different event shapes (tokens vs. summary vs. control).</li><li>Carry minimal context: Include a token index, optional finish_reason, and a final summary with usage for billing/UX.</li><li>Document timeouts and reconnection: For SSE you can also expose an SSE retry interval; for clients, document expected timeouts and backoff.</li><li>Error semantics: Prefer failing fast (non‑200) before starting the stream. If you must signal errors mid‑stream, reserve a control event type (e.g., event: “error”) and describe it in oneOf.</li></ul><h3 id="client-expectations">Client expectations</h3><ul><li>SSE clients: Use EventSource in browsers or an SSE library on servers. Parse event and data fields.</li><li>JSONL clients: Read line‑delimited JSON; process each line as one event.</li><li>Backpressure: Streaming responses are pull‑driven by TCP; keep events small and frequent for smoother UX.</li><li>Idempotency: If you support retries, include id/cursor fields so clients can resume safely.</li></ul><h3 id="why-this-is-useful-for-llms">Why this is useful for LLMs</h3><ul><li>Faster first token: Users see output immediately, improving perceived latency.</li><li>Progressive rendering: Stream tokens as they’re generated; UIs don’t block.</li><li>Richer telemetry: Emit intermediary signals (e.g., tool_call, reasoning, usage) as distinct event types.</li><li>Interoperability: A standardized spec makes SDKs and gateways simpler to build and maintain.</li></ul><hr /><p>See the OpenAPI 3.2 specification here.</p>

open_in_new Read original post