Skip to main content
After this guide, you can start a run of a deployed workflow, follow it to the point where it needs a person, answer it, recover a parked step, and cancel a run that should stop. In the desktop app, the Runs page lists each run with a card to approve, deny, or edit, and the Inbox collects the cards waiting on you; the steps below do the same from the CLI. To drive the graph on your machine without a run, see Test a workflow offline. Requires lua-cli 3.36.0 or later. Before you begin
  • A workflow pushed with lua push workflow --name <name> and made live with lua workflows deploy <name> -v latest.
  • A user session (lua auth configure): approve, resolve-step, and delete-run refuse an API key.
1

Start a run

start validates the input against the workflow’s inputSchema, creates the run, and returns at once. An idempotency key makes a retried script return the same run instead of a second one.
The first line is ✅ Run <runId> · queued. When the platform serves a model plan for the run, start prints it next: one row per agent step with the class or code it asked for, the model that resolves to today, its effort and reason, then one cost line in actions and the fallback promise — the same card a person is asked to consent to when the run is gated. --follow attaches watch, so --timeout and --wait-for-human apply. A run the organization cannot seat yet is gated and exits 6; a workflow with concurrencyPolicy: 'forbid' and a run in flight is meant to print the blocking run id and exit 1, but as of lua-cli 3.33.0 and the current platform the second run is created instead, so treat overlapping runs as possible until the fix ships. --correlation-key labels the run for Workflows.signalByKey and runs --correlation-key.
2

Follow it to a human boundary

watch streams the run’s events and ends when the run ends or when a person is needed.
Output
That notice ends the watch with exit 8 and names the verb that resumes the run: approve, resume for a step parked on ctx.suspend(), signal for a signal wait, raise-budget for a budget park, retry-step for a billing hold; an exception park prints ⚠️ run parked on an exception gate — a human decides next. A completed run exits 0, failed or timed_out 4, cancelled or abandoned 5, a consent gate 6.--wait-for-human keeps streaming through every boundary until the run is terminal or --timeout <s> fires with exit 7 and ⏱️ watch: --timeout 3s reached (run still live); Ctrl-C detaches with exit 0 and changes nothing.
3

Read the run and its steps

status prints the run header; --steps adds a table of every step with its kind, status, attempt, and error.
Output
Under the table, --steps prints one line per step that has something to add: the classified cause of a failure, what an attempt burned, its priced receipt, and, for an approval the platform re-addressed, ⇅ reviewDrafts: approver escalated creator → org-admins — the initiator is excluded (maker-checker). That line appears when your organization’s approval policy excludes the person who started the run and the step’s approver is the creator: rather than failing the step with nobody to ask, the platform asks the organization’s admins, minus the initiator, and records the escalation on the step; see Approvals and signals.The Budget: line says which unit that run is counting and what the unit buys, because both differ by organization:Only the third row leaves tokens out of the reckoning. On the first two the run is charged the way a chat turn is, which is why the same 40-step workflow can spend very different amounts on two different model choices — see choose a model per step. A Job-tier step is charged per model reply, so one long coding turn can be worth many units; see What a Job step costs. When the run’s steps carry priced receipts, --steps also prints a 🧠 By model table of calls and spend per model, with each model’s class beside it and a count of the fallback hops it served.Under the Budget: line, a run whose steps were priced per call also prints Tokens: — for example 4,812 uncached in · 261,004 cache read · 1,024 cache write · 3,190 out — the four figures the run’s own usage rows add up to. A figure the run recorded nothing for is left out rather than printed as zero. --steps gains Uncached, Cached (reads and writes together) and Output columns per step. Cached prompt tokens count toward a call’s price band at the fraction of a fresh token the provider charges Lua, and where Lua has no published fraction for a provider its calls are sized exactly as they were before; a cache read is a subset of the prompt, never an addition to it, which is why the columns are shown apart rather than summed.
The run budget is not your organization’s account credits. budget.maxCredits, --budget-credits and raise-budget --credits cap one run, and a run that reaches the cap stops and parks, waiting for you to raise it. Your account balance is separate: when it runs out, the refusal names a seat or credits at the account level, and raising a run budget does nothing for it.
--json prints the platform’s run document: status; suspendedFor and suspensions[], whose suspend.approvalId is the wfa_… id approve needs; gate (kind, code, stepId) on a gated run; nextAction (none, cancel_again, force, raise_budget, top_up); budget (maxCredits, spent, remaining, reserved, exceeded); usage.tokens (inputTokens uncached, cacheReadTokens, cacheCreationTokens, outputTokens) on a run priced per call; and steps[] with preview.input, preview.output and the same tokens block per step. An agent step carries its model choice too: taskClass, modelRequested (the code or class/… the definition asked for), modelResolved (the model that answered), modelReason, effortApplied (the effort that reached the model, or null when the step’s effort was recorded and dropped), and modelFallback[] when a leg failed and another model took the turn. modelResolved is the only field that says what ran. A Job-tier step carries job (model, modelSource — whether the step pinned it, the platform default supplied it, or your organization’s default did — and multiplier), which is what that step’s replies are priced on. An approval step the platform re-addressed carries suspend.approverFallback { from, to, reason }.
--strict maps a terminal status, and gated, to the watch exit codes for scripts that poll: 4, 5, or 6 instead of 0, so a loop never reads a run that is waiting for consent as still running.
4

Answer or repair the run

Each park names its verb. All take the run id first and refuse with a typed code when the step is not in the state they expect.A re-armed step runs under the same occurrence, so a once() effect that settled is not repeated. Nothing re-runs a terminal run: retry-step answers RUN_TERMINAL, so start a new run.A run that reaches its budget cap stops and parks — it does not fail, and nothing is half-charged: an attempt that crosses the cap mid-flight stops at the next tool boundary rather than in the middle of a model reply, and the run waits on the budget gate. raise-budget resumes it from where it stopped. The one case that ends rather than parks is a run whose last step finished having gone past the cap: there is nothing left to hold, so the run completes and is marked budget.exceeded. status reads finished past the cap, and lua workflows logs prints one run.budget_exceeded event naming the spend, the cap, the step and the plan. Treat it as a bill to look at, not a failure to repair — the run’s output is good.The model park is the one that clears itself. status prints ⏸️ Paused — a step's model request cannot be resolved under the current policy: with the gate code and step beneath it; the step is waiting, never failed, and the platform sweeps the gate once the request resolves again. Clear it by hand only when you would rather not wait: clear-gate re-arms the step at the same attempt, and the step parks again if the condition still holds.
The Workflows members behind these actions (resume, signal, signalByKey, startBatch, setGoal, goals.*) work in deployed code and under lua test alike; only Workflows.raiseBudget works in neither runtime, so raise a budget with lua workflows raise-budget or the REST route.
5

Cancel a run

A cancel is a request: running steps stop at their next boundary and the run moves to cancellation_requested.
The reply is ✅ cancel accepted · run <runId> is <status> plus what to do next: next: cancel again to escalate, or next: force available at <time> — lua workflows cancel <runId> --force. A forced cancel abandons the run; a run that already ended answers ℹ️ run <runId> was already <status> — nothing to cancel.
6

Verify

List the workflow’s runs, then read one run’s progress and lifecycle events.
Output
runs filters by --workflow, --status, --correlation-key, and --tag, sorts with --sort, and prints the next page’s command. logs returns the latest 500 progress and lifecycle events, --step <id> narrows to one step, and --follow switches to watch; --since is accepted but has no effect. The budget events read in plain language — run.budget_parked as paused on its run budget, run.budget_raised, and run.budget_exceeded as finished past the cap, each with the spend against the cap — and a Job-tier step emits step.job_model_resolved when it claims, naming the model it will run on, how that model was chosen, and its multiplier.Two of those lines are about model choice. step.model_resolved names the model a step resolved to and, in brackets, the class or task class it asked for. step.model_policy_warning says a step pinned a model your organization’s effective model set excludes; the step still runs, and the line carries the outcome (model_excluded, effective_model_set_unavailable, or model_pin_not_allowed), the pin the definition asked for, and a message. That warning is written before the turn is dispatched, so it never names what answered — read modelResolved off the step for that.

Options you may need

Activate, deactivate, and deploy

lua workflows activate <name> turns the workflow’s schedules and triggers on and deactivate pauses them; neither changes the live version. activate -v <version> is the same as deploy <name> -v <version>: it makes that version live for new runs and records an agent version, so lua version promote <n> is the rollback. A run keeps the version it started on; neither a deploy nor a promote moves a run in flight. From lua-cli 3.36.0, lua push all pushes workflows and makes each pushed version live at once (earlier releases skip them); lua deploy has no workflow type; see Release an agent.

Run statuses and exit codes

Other refusals follow the lua workflows table: 1 API refusal, 2 usage, 3 not found, 9 not signed in, 10 forbidden, 11 unavailable. --json prints the platform’s { success, data } or { success: false, error } envelope and keeps the exit code.

If it isn’t working

The step is not in a state retry-step or resolve-step can act on. status --steps names it: a running step needs time, a suspended one needs approve, resume, or signal.
The run has ended, so no step can be retried or resolved. Start a new run.
Only a run parked on its budget takes a raise, and only above its current cap. A run at its raise cap must be canceled and started again with a larger --budget-credits.

Next steps

Approvals and signals

Approver kinds, editable payloads, and delivering signals from code.

lua workflows reference

Every verb, flag, and refusal code.

Workflows runtime API

Start, signal, resume, and cancel runs from tools and webhooks.

REST API

The routes behind each verb.