Workflows starts runs of a deployed workflow and steers them: read a run, list runs, cancel, resume a suspended step, deliver signals, start batches, raise a parked run’s budget, and manage goals. Every member returns as soon as the platform accepts the call; none waits for a run to finish. Available in tools, jobs, webhooks, triggers, processors, and workflow code steps. From a code step, start() creates a detached run; to wait on a nested run, use the workflow() step of the workflow builder.
Verified against lua-cli 3.33.0.
Quick example
Methods
start()
Starts a run and returns its id and status.string
required
The workflow’s
name on this agent, resolved first, or a workflow id.unknown
The run’s input, validated against the workflow’s input schema. The REST route that
lua test calls refuses JSON over 256 KB; deployed agents don’t apply that cap.string
A second
start() with the same key returns the existing run with idempotentReplay: true and deduplicated: true instead of creating another.{ maxCredits?: number; maxSteps?: number; maxDurationSeconds?: number }
Caps for this run. A run that reaches a cap parks with
nextAction: 'raise_budget'.number
From 0 to 55. The platform holds the request for up to this long; when the run reaches a terminal or suspended state inside the window, the result is the run detail with
output, otherwise the accepted { runId, status }. The execution model is unchanged either way.Record<string, unknown>
Seed for the run’s
state.string
A key of your own that
signalByKey() can address the run by.string[]
Labels for
list({ tags }).{ channel: string; threadId: string }
Where the run’s replies go.
{ userId: string }
The end user the run acts for.
string
Pins a version. Defaults to the active version.
StartWorkflowRunResult
The accepted run.
RUNS_IN_FLIGHT when the workflow’s concurrencyPolicy is forbid and a run is in flight (under lua test the error carries blockingRunId); WORKFLOW_NOT_FOUND; PAYLOAD_TOO_LARGE (413) under lua test when input exceeds 256 KB; CONTROL_UNAVAILABLE (503) while the control plane is unavailable; waitSeconds must be 0..55 (got <n>) before any request is made.
Local runs only. In deployed agents
start forwards only idempotencyKey, budget, correlationKey, tags, and workflowVersionId and resolves { runId, status }; see Deployed runtime differences for get, list, and cancel.get()
Returns one run’s summary.WorkflowRun summary. hasOutput and a 2 KB outputPreview are included; output itself is not. A run whose outputs you may not read comes back with restricted: true and no output, never as an error. Read a run’s steps and full output with lua workflows.
Example
RUN_NOT_FOUND.
list()
Returns run summaries, optionally filtered.number
Maximum number of runs.
WorkflowRunStatus
Only runs in this status.
string
Only runs of this workflow, by name or id.
string
Only runs started with this key.
string[]
Only runs carrying these tags.
string
Sort order accepted by the platform.
WorkflowRun summaries, with the same output rules as get().
Example
cancel()
Requests or forces a cancellation.'request' | 'force'
default:"request"
request asks the run to stop at its next checkpoint; force abandons it once forceAvailableAt has passed.string
Recorded on the run.
CancelRunVerdict
What the cancellation did. A
force before forceAvailableAt returns transitioned: false with nextAction: 'cancel_again' and the time; a run that is already terminal answers state: 'terminal'. Neither is an error.RUN_NOT_FOUND.
resume()
Resumes a suspended step with the data it is waiting for.string
required
The run.
string
required
The suspended step’s id.
unknown
required
Validated against the step’s resume schema.
{ resumed: true, runStatus }, or { resumed: false, reason: 'already_resumed', recorded: { at, by? }, runStatus } when another caller resumed the step first. The loser of the race gets the recorded outcome, never an error.
Example
RUN_NOT_FOUND, STEP_NOT_FOUND.
signal()
Delivers a named signal to a run.string
required
The signal name a
waitForSignal step listens for.unknown
Delivered to the step.
string
A repeated key returns
duplicate: true instead of delivering twice.SignalRunResult
RUN_NOT_FOUND.
signalByKey()
Delivers a signal to the live run that carries a correlation key.string
required
The workflow, by name or id.
string
required
The key the run was started with.
string
required
The signal name.
boolean
Deliver to every live run with the key. Without it, more than one match is an error.
{ runIds, delivered }. At run time the object also carries a results array with one { runId, accepted, consumed?, duplicate?, signalId?, code? } per run; it isn’t in the typings.
Example
WORKFLOW_NOT_FOUND, CORRELATION_KEY_NOT_FOUND, CORRELATION_KEY_AMBIGUOUS (several live runs carry the key and allowMultiple is not set).
startBatch()
Starts up to 200 runs of one workflow in a single call.array
required
One entry per run, up to 200;
idempotencyKey is required on each. A malformed item refuses the whole batch.'reject' | 'gate'
default:"gate"
What happens to runs that don’t get a slot:
gate parks them, reject refuses them.object
A budget applied to each run that has none of its own.
batchId, the number accepted, and one item per input with its runId and status (queued, gated, replayed for an idempotency match, or rejected with a code).
Example
WORKFLOW_NOT_FOUND, BATCH_ITEM_INVALID, BATCH_TOO_LARGE (400) past 200 items, RUNS_IN_FLIGHT (the whole batch is refused).
raiseBudget()
Raises the caps of a parked run so it can continue. Increases only.{ raised: true, budget, runStatus, resumed }, or { raised: false, reason: 'already_raised' } when another caller raised it first.
Example
VALIDATION_FAILED and CAP_EXCEEDED (400), NOT_RUN_CREATOR (403), BUDGET_NOT_RAISABLE (409).
No runtime serves this member in lua-cli 3.33.0:
lua test throws WorkflowApiError with code WORKFLOWS_API_UNAVAILABLE, and deployed agents throw WorkflowsApiError with code not_implemented (501). Raise a budget with lua workflows raise-budget instead.setGoal()
Creates a goal: the platform runs the workflow repeatedly until a judge decides the objective is met.string
required
The id of the agent the code runs in. Goals are scoped to that agent; any other id is refused with
FORBIDDEN.object
required
workflowId or workflow (a name), objective, judge, and maxRuns, plus optional cadence, evaluation, budget, maxTotalCredits, initialState, input, idempotencyKey, and workflowVersionId. The judge is { agentId?, role?: { name, instructions, tools }, schema?, predicate?: { path, op, value? } }.goalId, status (active, paused, done, or closed), runsUsed, iteration, currentRunId, verdict, pauseReason, and the fields you set. Typed loosely as Record<string, unknown>.
Example
FORBIDDEN (403) for another agent’s id, VALIDATION_FAILED, GOAL_MAX_RUNS_INVALID, GOAL_CAP.
goals
Reads and controls goals. Every member takes the bound agent’s id first and refuses any other id withFORBIDDEN.
setGoal(); get() adds the goal’s runs history. pause() needs an active goal, resume() a paused one, and close() is terminal.
Example
FORBIDDEN, GOAL_NOT_FOUND, GOAL_NOT_ACTIVE.
Deployed runtime differences
Underlua test every member calls the platform’s REST routes and returns the typed shapes documented earlier. In deployed agents the members are served in process, and several return less than the typings declare.
start()forwardsidempotencyKey,budget,workflowVersionId,correlationKey(up to 200 characters), andtags(up to 10, each up to 64 characters). It dropswaitSeconds,initialState,replyTo, andonBehalfOf, skips the 256 KBinputcheck, and resolves{ runId, status }, withstatusfailedwhen the run was recorded but could not start, for example becauseinputfailed the workflow’s input schema.get()resolvesrunId,workflowId,workflowVersionId,status,trigger,createdAt, and, when set,startedAt,completedAt,durationMs,gate,reason,output, anderror. There is nonextAction,budget,outputPreview, orrestricted.list()filters bystatusand an untypedworkflowIdonly;workflow,correlationKey,tags, andsortare ignored.limitis clamped to 1 to 200 and defaults to 50.cancel()resolves{ status, nextAction, forceAvailableAt? }.resume()throws coderesume_unavailable(501) on a platform without the resume handler.signal()andsignalByKey()throw codesignal_unavailable(501) on a platform without the signal handler. When served,signal()resolves{ accepted, reason? }andsignalByKey()resolves{ runIds, delivered }with noresultsarray; a delivery the platform refuses comes back as{ accepted: false, reason }, not as an error.startBatch(),setGoal(), andgoals.*throw codenot_implemented(501) when the platform’s optional batch or goal provider is absent, so handle that error.startBatch()also resolvesnameOrIdas a workflow id only, never a name.raiseBudget()always throws codenot_implemented(501). Raise a budget withlua workflows raise-budgetor thePOST /workflows/:agentId/runs/:runId/budgetroute instead.
Errors
A refusal throws anError with a code (the platform’s discriminator, as listed per method) and a statusCode. Under lua test the error’s name is WorkflowApiError and it carries the detail fields the platform sent, such as blockingRunId. In deployed agents the name is WorkflowsApiError; detail fields are attached only when the platform’s refusal carried them (startBatch and goals), so a RUNS_IN_FLIGHT from start has no blockingRunId. Neither class is exported; branch on code.
Workflows API is not available in this runtime with code workflows_unavailable.
Types
Workflows, WorkflowsApi, and WorkflowRunTrigger are exported. The result shapes are not; name them from the method that returns them.
WorkflowRunStatus
queued, running, cancellation_requested, gated, suspended, waiting, completed, failed, cancelled, abandoned, or timed_out.
WorkflowRun
string
The run’s id; older responses spell it
id.string
Where the run belongs.
WorkflowRunStatus
Current status.
WorkflowRunTrigger
chat, sdk, api, schedule, webhook, template, workflow, or device.string | string[]
Optional identity you or a parent run gave it.
{ kind: string; reason?: string; code?: string; stepId?: string; expiresAt?: number }
Why a
gated run holds no slot.'cancel_again' | 'force' | 'none' | 'raise_budget' | 'top_up'
What the platform suggests next.
{ requestedAt: number; requestedBy: string; forceAvailableAt: number; forcedAt?: number; forcedBy?: string; forceReason?: string }
The pending or applied cancellation.
string
Why a
failed run failed.boolean
true when outputs are withheld from the caller.boolean | string | unknown
Whether a result exists, its 2 KB preview, and the full value when the view includes it.
{ creditsUsed?: number; actionsUsed?: number; steps?: number; inputTokens?: number; outputTokens?: number; metering?: 'flat' | 'priced' }
Settled spend.
{ maxCredits?: number; maxSteps?: number; maxDurationSeconds?: number; spent?: object; remaining?: number }
Caps and remaining allowance.
string
ISO 8601 timestamps.
See also
- Workflow builder — define the workflows you start here
- Operate runs — watch, approve, retry, and cancel runs from the CLI and the desktop
- Approvals and signals — how-to
- Goals and schedules — how-to
lua workflows— the CLI twin of every member

