The Durable Streams protocol defines six core HTTP operations for interacting with streams. All operations are applied to a stream URL, and the protocol is defined by the HTTP methods, query parameters, and headers used.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/durable-streams/durable-streams/llms.txt
Use this file to discover all available pages before exploring further.
Create Stream
Creates a new stream at the specified URL. Request:- Return
200 OKif the existing stream’s configuration (content type, TTL/expiry, and closure status) matches the request - Return
409 Conflictif configuration does not match
Request Headers
Sets the stream’s content type. If omitted, the server may default to
application/octet-stream.Sets a relative time-to-live in seconds from creation. Must be a non-negative integer in decimal notation without leading zeros, plus signs, decimal points, or scientific notation (e.g.,
3600 is valid; +3600, 03600, 3600.0, and 3.6e3 are not).Sets an absolute expiry time as an RFC 3339 timestamp. If both
Stream-TTL and Stream-Expires-At are supplied, servers should reject the request with 400 Bad Request.When set to
true, the stream is created in the closed state. Any body provided becomes the complete and final content of the stream. This enables atomic “create and close” semantics for single-message or empty streams.Request Body
Optional initial stream bytes. If provided, these bytes form the first content of the stream.Response Codes
201 Created: Stream created successfully200 OK: Stream already exists with matching configuration (idempotent success)409 Conflict: Stream already exists with different configuration400 Bad Request: Invalid headers or parameters429 Too Many Requests: Rate limit exceeded
Response Headers
The stream URL (included on 201 Created)
The stream’s content type
The tail offset after any initial content
Present when the stream was created in the closed state
Example
Append to Stream
Appends bytes to the end of an existing stream. Request:Request Headers
Must match the stream’s existing content type when a body is provided. May be omitted when the request body is empty (close-only requests with
Stream-Closed: true).Set to
chunked for streaming bodies. Servers should support HTTP/1.1 chunked encoding and HTTP/2 streaming semantics.A monotonic, lexicographic writer sequence number for coordination. If provided and less than or equal to the last appended sequence, the server returns
409 Conflict. Sequence numbers must be strictly increasing.When set to
true, the stream is closed after the append completes. This is an atomic operation: the body (if any) is appended as the final data, and the stream transitions to the closed state. Close-only requests (with empty body) are idempotent.Client-supplied stable identifier for idempotent producers (e.g., “order-service-1”, UUID). Must be provided together with
Producer-Epoch and Producer-Seq.Client-declared epoch for idempotent producers, starting at 0. Increment on producer restart to establish a new session. Must be a non-negative integer ≤ 2^53-1.
Monotonically increasing sequence number per epoch for idempotent producers. Starts at 0 for each new epoch. Must be a non-negative integer ≤ 2^53-1.
Request Body
Bytes to append to the stream. Servers must reject POST requests with an empty body (Content-Length: 0 or no body) with400 Bad Request, unless the Stream-Closed: true header is present.
Response Codes
204 No Content: Append successful (or stream already closed when closing idempotently)200 OK: Append successful with idempotent producer (new data)400 Bad Request: Malformed request (invalid header syntax, missing Content-Type, empty body withoutStream-Closed: true)404 Not Found: Stream does not exist405 Method Not Allowedor501 Not Implemented: Append not supported for this stream409 Conflict: Content type mismatch, sequence regression, or stream is closed413 Payload Too Large: Request body exceeds server limits429 Too Many Requests: Rate limit exceeded403 Forbidden: Stale producer epoch (with idempotent producer headers)
Response Headers
The new tail offset after the append
Present when the stream is now closed (either by this request or previously)
Echoed back on success with idempotent producer, or current server epoch on stale epoch (403)
On success with idempotent producer, the highest accepted sequence number for this (stream, producerId, epoch) tuple
On 409 Conflict (sequence gap), the expected sequence
On 409 Conflict (sequence gap), the received sequence
Example
Idempotent Producer Example
Close Stream
Closes a stream without appending data. Request:Response Codes
204 No Content: Stream closed successfully (or already closed—idempotent)404 Not Found: Stream does not exist405 Method Not Allowedor501 Not Implemented: Append/close not supported for this stream
Response Headers
The tail offset (unchanged, since no data was appended)
Confirms the stream is now closed
Example
Delete Stream
Deletes the stream and all its data. Request:Response Codes
204 No Content: Stream deleted successfully404 Not Found: Stream does not exist405 Method Not Allowedor501 Not Implemented: Delete not supported for this stream
Example
Stream Metadata
Checks stream existence and returns metadata without transferring data. Request:Response Codes
200 OK: Stream exists404 Not Found: Stream does not exist429 Too Many Requests: Rate limit exceeded
Response Headers
The stream’s content type
The tail offset (next offset after the current end)
Remaining time-to-live in seconds, if applicable
Absolute expiry time as RFC 3339 timestamp, if applicable
Present when the stream has been closed. Absence indicates the stream is still open.
Servers should return
no-store to avoid stale tail offsets and closure statusExample
Read Stream - Catch-up
Returns bytes starting from the specified offset for historical replay. Request:Query Parameters
Start offset token. If omitted, defaults to the stream start (offset
-1). Use -1 to read from the beginning or now to start from the current tail.Response Codes
200 OK: Data available (or empty body if offset equals tail)400 Bad Request: Malformed offset or invalid parameters404 Not Found: Stream does not exist410 Gone: Offset is before the earliest retained position (retention/compaction)429 Too Many Requests: Rate limit exceeded
Response Headers
Derived from TTL/expiry. For shared streams:
public, max-age=60, stale-while-revalidate=300. For user-specific streams: private, max-age=60, stale-while-revalidate=300.Entity tag for cache validation, format:
{internal_stream_id}:{start_offset}:{end_offset}Cursor to echo on subsequent long-poll requests for CDN collapsing. Optional for catch-up, required for live modes.
The next offset to read from for subsequent requests
Must be
true when the response includes all data available in the stream at the time the response was generated. Should not be present when returning partial data due to server-defined chunk size limits.Must be present when the stream is closed and the client has reached the final offset. Indicates EOF (end-of-file).
Response Body
Bytes from the stream starting at the specified offset, up to a server-defined maximum chunk size.Example
Read Stream - Live (Long-poll)
If no data is available at the specified offset, the server waits up to a timeout for new data to arrive. Request:Query Parameters
The offset to read from. Must be provided.
Set to
long-poll to indicate long-polling mode.Echo of the last
Stream-Cursor header value from a previous response. Used for collapsing keys in CDN/proxy configurations.Response Codes
200 OK: Data became available within the timeout204 No Content: Timeout expired with no new data400 Bad Request: Invalid parameters404 Not Found: Stream does not exist429 Too Many Requests: Rate limit exceeded
Response Headers (on 200)
Servers must include this header for CDN collapsing
The next offset to read from
Indicates the client is caught up with all available data
Must be present when the stream is closed (EOF signal)
Response Headers (on 204)
The current tail offset
Must be
true to indicate the client is caught upMust be included when the stream is open. May be omitted when
Stream-Closed is true.Must be present when the stream is closed. A
204 No Content with Stream-Closed: true indicates EOF.Example
Read Stream - Live (SSE)
Returns data as a Server-Sent Events (SSE) stream. Request:Query Parameters
The offset to start reading from
Set to
sse to indicate SSE streaming modeResponse Codes
200 OK: Streaming body (SSE format)400 Bad Request: Invalid parameters404 Not Found: Stream does not exist429 Too Many Requests: Rate limit exceeded
Response Format
Data is emitted in Server-Sent Events format. The response usesContent-Type: text/event-stream.
For streams with content-type text/* or application/json, data events carry UTF-8 text directly. For all other content types (binary streams), servers automatically base64-encode data events and include the response header Stream-SSE-Data-Encoding: base64.
Events:
-
data: Emitted for each batch of data. For binary streams, the payload is base64-encoded per RFC 4648. For JSON streams, implementations may batch multiple messages into a single SSE data event by streaming a JSON array across multipledata:lines. -
control: Emitted after every data event. Format is a JSON object with camelCase field names:streamNextOffset(required): The next offset to read fromstreamCursor(required when stream is open): Cursor for reconnection. May be omitted whenstreamClosedis true.upToDate(required when true): Set totruewhen the client is caught up with all available datastreamClosed(required when true): Set totruewhen the stream is closed and all data has been sent
SSE Event Examples
Normal data event:Example
Stream Closure Behavior
When the stream is closed:- The final
controlevent must includestreamClosed: true - After emitting the final control event, servers must close the SSE connection
- Clients receiving
streamClosed: truemust not attempt to reconnect
Connection Lifecycle
- Server should close connections roughly every ~60 seconds to enable CDN collapsing
- Client must reconnect using the last received
streamNextOffsetvalue from the control event - Client must not reconnect if the last control event included
streamClosed: true