Runs & streaming
Run lifecycle, streamed events, and cost control.
Starting a run executes a workflow graph. Because runs can take a while (model inference, multi-step graphs), the API streams progress events instead of blocking on a single response.
Run events
A run emits a stream of events, a discriminated union on type:
| Event | Payload | Meaning |
|---|---|---|
step.started | nodeId | A node began executing |
step.progress | nodeId, message | Progress within a node |
step.completed | nodeId, usage: { costUsd, ms } | Node finished, with cost/time |
step.failed | nodeId, error: { code, message } | Node failed |
run.completed | outputs: Record<string, Artifact> | Terminal — the results |
run.failed | error: { code, message } | Terminal — the run failed |
Read the stream to completion and collect run.completed.outputs. Treat
run.failed and step.failed as terminal: surface the error message — don't
retry blindly.
Over HTTP the stream is Server-Sent Events; the SDK parses it into an async iterable for you:
const handle = await client.startRun("abc123@latest", inputs);
for await (const event of handle.events()) {
if (event.type === "step.completed") {
console.log(
`${event.nodeId}: $${event.usage.costUsd} in ${event.usage.ms}ms`,
);
}
if (event.type === "run.completed") return event.outputs;
if (event.type === "run.failed") throw new Error(event.error.message);
}Cost control
Runs spend money (model inference), charged to your organization's prepaid
balance. A run is admitted while the balance is positive, runs to completion,
and is charged its metered cost when it ends. There is no per-run spend cap: a
run is never failed for what it cost, so an expensive step never leaves you
paying for a run that returned nothing. A run that exhausts the balance still
completes; the next run is refused with 402 insufficient_credit until you add
funds in the Studio's billing settings.
Practical habits:
- Know what a run will cost before you start it:
runs.listandruns.getreportcostUsdper run and per step, so comparable past runs are the estimate. - Generate at the smallest useful size / fewest variations; scale up winners.
- Persist output
refURLs and don't regenerate assets you already have. - Watch
step.completed.usage.costUsdto learn what each node actually costs.
Single-node runs
For one-off inference you don't need a workflow at all — runs.node executes a
single node by id with plain inputs and returns its outputs directly (no
stream). Available through the SDK and the
runs_node MCP tool.