Runs onWrit Cloud
On this page
Streaming.
A streaming session keeps a real browser open on a workflow and makes it callable: replay a slice of the recorded steps as a handler, expose script functions, or point an OpenAI client at it and let the model call both as tools — grounded in the live page.
model ▸ the layers
One session, three layers.
A streaming session starts from a workflow. Its recorded steps split at a setup boundary: the setup slice runs once when the session starts — navigate, sign in, arrive — and the remaining steps become material for handlers, the callable surface of the live session. An optional advanced script adds functions of your own on top.
The split is the workflow’s setup_steps_count: everything before it is setup, everything after it is handler material. Setup runs exactly once per session — callers never pay the sign-in again.
handlers ▸ the callable surface
Handlers.
A handler is a named, callable unit on a live session. Its definition has these fields:
| Field | Role |
|---|---|
name | The handler’s callable name (up to 100 characters). |
type | Default "steps" — the handler replays part of the recorded workflow. |
step_range | [start, end] — the slice of recorded steps to replay. Prerequisite steps are derived automatically, so a mid-recipe slice still lands on the right page state. |
input_variables | The values the caller passes at invoke time; they fill the slice’s placeholders. |
extract_fields | What the handler reads off the page and returns. |
code | Optional (up to 50 000 characters): a script-typed handler that runs code instead of replaying steps. |
trigger_config | Optional wiring for handlers fired by something other than a direct call. |
POST /api/streaming/sessions/{session_key}/handlers
{
"name": "search_orders",
"type": "steps",
"step_range": [4, 9],
"input_variables": ["order_id"],
"extract_fields": ["order_status", "order_total"]
} script ▸ your own functions
The advanced script.
A workflow can carry an advanced_script step. Its script is injected into the live session, and the functions it declares join the handler list callers see — same invoke surface, same tool exposure:
| Config field | Role |
|---|---|
code | The script itself. |
persistent | Default true — the script survives page navigations instead of dying with the page. |
functions | The list of callables the script declares; each becomes a named handler. |
{
"type": "advanced_script",
"config": {
"code": "…",
"persistent": true,
"functions": ["summarize_thread"]
}
} An older per-workflow config location for the script is still honoured on legacy workflows. You don’t have to write the script by hand: the generate-streaming-script helper drafts it for you — see AI sessions.
invoke ▸ call a handler
Invoking, and changing handlers live.
Call a handler by name on a running session. The request body has two fields:
| Field | Role |
|---|---|
data | The handler’s input_variables, as an object. |
timeout | How long to wait for the handler, 1–120 seconds (default 30). |
POST /api/streaming/sessions/{session_key}/invoke/search_orders
{ "data": { "order_id": "A-1042" }, "timeout": 30 }
# Manage handlers on the running session:
POST /api/streaming/sessions/{session_key}/handlers
DELETE /api/streaming/sessions/{session_key}/handlers/{handler_name} Handlers are not frozen at session start: POST …/handlers with a handler definition adds one to the running session, and DELETE …/handlers/{handler_name} removes it.
openai ▸ drop-in surface
The OpenAI-compatible surface.
Existing OpenAI SDKs and agent frameworks can drive a session with no custom client. Messages are answered from the live session — what the browser actually shows — and the session’s handlers and script functions surface as tools the model can call:
| Endpoint | Accepts |
|---|---|
POST …/v1/chat/completions | model ("streaming"), messages, stream, tools / tool_choice — plus the legacy functions / function_call shape. |
POST …/v1/responses | input, instructions, stream, max_output_tokens, tools. |
GET …/v1/models | Lists the session-backed model. |
from openai import OpenAI
client = OpenAI(
base_url="https://api.usewrit.app/api/streaming/workflows/{workflow_id}/v1",
api_key="wt_YOUR_KEY",
)
r = client.chat.completions.create(
model="streaming",
messages=[{"role": "user", "content": "What does order A-1042's page say?"}],
)
print(r.choices[0].message.content) The same three endpoints exist at two bases: /api/streaming/workflows/{workflow_id}/v1 (a session is started for you from the workflow) and /api/streaming/sessions/{session_key}/v1 (you address a session you already started).
sessions ▸ start and options
Starting a session.
A session start request takes:
| Field | Role |
|---|---|
workflow_id | The workflow whose setup slice and steps back the session. |
target_url | Where the browser should start, when not implied by the workflow. |
max_duration_seconds | 60–86400, then clamped to your plan’s cap. |
headless | Default true. |
execution_target | Default "auto" — where the browser runs. |
form_data | Inputs for the setup slice’s placeholders. |
multi_conversation | Default false — thread several conversations through one session. |
context_mode | Default "shared" — how threads share page context. |
max_concurrent_threads | Default 5. |
session_persistence: saved sign-in state (cookies and localStorage) can be restored into a new session, so setup doesn’t re-run a login the browser already holds.
POST /api/streaming/sessions/start
GET /api/streaming/sessions
POST /api/streaming/sessions/{session_key}/end
POST /api/streaming/sessions/{session_key}/invoke/{handler_name}
GET /api/streaming/sessions/{session_key}/events # SSE
WS /api/streaming/sessions/{session_key}/ws
# OpenAI-compatible, at both bases:
POST /api/streaming/workflows/{workflow_id}/v1/chat/completions
POST /api/streaming/workflows/{workflow_id}/v1/responses
GET /api/streaming/workflows/{workflow_id}/v1/models
# …and the same three under /api/streaming/sessions/{session_key}/v1/ A session reports queued, starting, running, ending, ended or failed; when it ends, end_reason is user_ended, timeout, agent_lost or error.
Plan caps: session duration from 5 minutes on Free up to 60 minutes on Scale and Enterprise; concurrent sessions start at 2 on Free.
worked ▸ a support portal
Worked example: a support portal, callable.
One session, both layers, then the OpenAI surface on top:
- The session starts from the support-portal workflow. The setup slice signs in with the saved persona state and lands on the orders view — once.
- A handler named search_orders (type "steps") replays the recorded search slice via its step_range, takes input_variables ["order_id"], and returns extract_fields like the order’s status and total.
- The advanced script declares summarize_thread — a function that reads the open support thread straight from the page. It joins the handler list beside search_orders.
- An OpenAI client pointed at the session sees both as tools. Mid-conversation the model calls search_orders with an order id, then summarize_thread — every answer grounded in what the live portal actually shows.
pricing ▸ no per-call charge
No charge per call.
Chat, responses and invoke calls carry no per-call charge: they inject your message into the session’s live browser page — no platform model sits in the middle, so there is nothing to meter per call. Only the session’s browser time is settled when it ends.
next ▸ where to go
Keep going.
- Workflows — the recorded steps a session’s setup and handlers replay.
- AI sessions — goal-driven runs, and the helper that writes your advanced script.
- Billing & usage — how browser time is settled at session end.