.in carries records from your clients to the task, .out carries records from the task back to your clients. The sessions SDK wraps these as session.in.* and session.out.*. This page documents the underlying HTTP endpoints for callers that aren’t using the TypeScript SDK.
All channel endpoints live under /realtime/v1/sessions/{session}/{io}, where:
{session}is the session’s friendly ID (session_…) or yourexternalId. One token authorizes both forms.{io}is eitherinorout.
Append a record
Append a single record to a channel.Append to .in
413). The response is { "ok": true }.
Set the X-Part-Id header to a unique value per record to make the append idempotent: replaying the same X-Part-Id does not duplicate the record. Appending to a closed or expired session returns 400.
Read a channel over SSE
Subscribe to a channel as a Server-Sent Events stream. New records are delivered as they arrive.Read .out
Each SSE event carries:
id:— the record’s sequence number. Use the most recent one asLast-Event-IDto resume.data:— a JSON record{ "data": <record>, "id": <id> }. For.outon achat.agentsession,datais a UI message chunk (text, reasoning, tool call, or a custom data part).
Control records
Some.out events are control records rather than data. A control record has an empty body and carries a trigger-control header naming its subtype:
Route control records by their subtype instead of treating them as message content. The TypeScript SDK does this for you —
session.out.read filters control records out of the chunk stream and surfaces them through onControl.
Drain records non-streaming
Fetch a batch of records without holding an SSE connection open. Useful for polling or for reading a tail at startup.Drain .out
afterEventId to return only records after that sequence number; omit it to read from the start of the retained window. The response is:
data, id, seqNum, and an optional headers array (present on control records). Page forward by passing the highest seqNum you received as the next afterEventId.
Authorization
The action you can take depends on your token and the channel:
Reads work in both directions for a
read:sessions token. Writes split by direction: a write:sessions token can append to .in, but .out is reserved for the task and requires a secret key. See session scopes for how to mint a token.
Using the SDK instead
If you’re writing TypeScript, thesessions SDK is the ergonomic path. sessions.open(idOrExternalId) returns a SessionHandle whose session.in and session.out channels call these endpoints for you, with auto-retry, Last-Event-ID resume, and control-record routing built in:
Your backend
session.in and session.out for the full handle API.
