Skip to main content
After a session is created, the control WebSocket is the place where your application changes what happens next. The video remains on the HLS playback URL; the WebSocket carries commands and responses.

Interaction overview

You can interact with a running story in four ways: A common interaction looks like this: A client does not need to use every control. Most experiences can start with instruction.create, add state when the product has numbers or rules to display, and add references only when a new character or object appears.

Connect once and start playback

Create a session first, then open the returned control URL with the session-specific secret:
session.ready means the opening media is ready to play. Its playback.url is an HLS address; pass it to an HLS-capable player instead of constructing a URL yourself. The second WebSocket subprotocol value is the session secret. Do not send your general API key over this channel.

Send the next instruction

instruction.create is the primary interaction. Use it whenever the user chooses an action, asks a question that should become part of the story, or wants to change the direction of the next scene.

Request

Describe one coherent piece of what should happen next. Include the important action, characters, tone, and visual result in the text. You do not need to split it into shots or dialogue turns.

Response events

The normal lifecycle is:
Treat instruction.scheduled as the current timeline estimate. A later scheduled event can revise its position. Treat instruction.started and instruction.completed as playback-time milestones, not as a guarantee that every viewer has rendered the same frame at the same wall-clock instant. If the instruction cannot be prepared or the session is ending, the server may emit:
An instruction that arrives after ending has started may instead receive instruction.cancelled. Use a new client_request_id for a genuinely new instruction. Do not resend the same text with a new id just because the acknowledgement is delayed.

Manage world state

State is for structured values your product needs to show or enforce: health points, score, location, inventory, clue count, weather, or quest progress. It is separate from the natural-language instruction, but an instruction can carry state changes that should happen with that scene. A battle might start with:

Change a value immediately

Use state.update when the application has already decided that a value should change now:
The response is a state.updated event containing the complete state, not just the two changed fields:

Tie a value to a scene

Use state_ops on instruction.create when the value should become effective with that instruction:
The state change is applied when this instruction finishes playing. Use instruction_start for a fact that becomes true when the scene begins, and instruction_end for a change that takes effect when the scene ends. For a previously accepted instruction, state.update can target it explicitly:
Supported operations are set, delete, append, and remove. The service accepts or rejects the whole batch; it does not partially apply an invalid batch.

Read the current state

Use state.get when the UI needs a fresh snapshot:
The response goes only to the connection that asked:
Replace the local state with the full state object from state.updated or state.snapshot. Do not infer a separate hidden state schema.

Add a reference when the story needs one

Add a reference when a new character, object, location, or voice becomes relevant. Register the file with the public upload API first, then send its file_id over the control channel.
Then add it to the session:

Response events

Success:
Failure:
Wait for assets.added before sending an instruction that depends on the new reference:
Adding a reference does not force it into the current frame. It only makes the material available to later story instructions. A session can contain more references than one generated segment uses; the service chooses the references relevant to each segment.

Stop the session

Stop immediately when the user leaves or the application must end the experience now:
Request a closing scene when the user wants a conclusion:
The server sends session.ending while the closing is being prepared and session.ended after the session reaches its terminal state. Once session.ending arrives, stop sending new story instructions.

Reconnect and event ordering

Every historical server event has a monotonically increasing id. Save the latest id after processing each event. If the connection drops, reconnect with:
The server sends events after that cursor and then resumes live delivery. state.snapshot.last_event_id is the cursor included in that state read. Process events by id. A later instruction.scheduled can revise an earlier timeline estimate. If a session has already ended, use GET /v1/streaming-world/sessions/{session_id} for the final status and timeline.

Handle control errors

Use the public error.code to decide what the client should do: Use Client messages for the complete request fields and Server events for every response schema.