Using bounded loops
Use a loop step when a fixed sequence of steps needs to repeat against shared Flow variables until a verdict passes. Common examples are draft, review, and revise; generate, validate, and repair; and retrieve, assess, and refine.
The dashboard Flow editor doesn’t have a loop step. Author bounded loops through the API, the Runtype MCP server, or the TypeScript SDK.
How a loop runs
A bounded loop runs its body once, then evaluates until. It repeats the body only while the expression is false. maxIterations is a hard backstop: the loop stops after that many rounds even when the verdict never passes.
A loop step takes these fields in its config:
steps(required): one or more step objects that form the body. Body steps use the same shape as top-level Flow steps.until(required): a JavaScript expression that ends the loop when it’s true. It uses the same syntax as a conditional step’s condition. For more information, see Write a condition.maxIterations(required): an integer from 1 to 10.iterationVariable(optional): a variable name that receives the current round number, starting at 1. The name can’t start with_, because that prefix is reserved for system variables.
A loop step has no outputVariable and produces no result value of its own. After the loop, read the variables that the body wrote. The standard step-level when and enabled settings work on a loop step the same way they work on any other step. A falsy when skips the whole loop. For more information, see Skip a single step with when.
Add a loop step
This loop reviews a draft and revises it until the review passes, for at most three rounds:
The body reads and writes the same Flow-level variables on every round. In this example, each revision replaces draft, and the next review reads the revised value. Because iterationVariable is set, Runtype writes the current round to reviewRound before each round’s body runs.
MCP flow tools accept the same type: "loop" step shape in inline, create, and update requests.
Build a loop with the TypeScript SDK
FlowBuilder and the client.flows builder both provide a .loop() method. Its steps field takes the same step objects as the JSON config.steps array:
Check your loop
Validate the Flow before you run it. Call the validate endpoint (POST /v1/public/flows/validate) or the validate_flow MCP tool. Validation reports missing or out-of-range loop fields, such as a maxIterations above 10, and these errors for loop bodies:
NESTED_LOOP_UNSUPPORTED: a loop body contains anotherloopstep.DUPLICATE_STEP_ID: a body step reuses an id. Step ids must be unique across the whole Flow, including steps inside loop bodies.
For the full list of checks, see Flow validation warnings.
In a run’s step results, the loop appears as a container step with the loop’s name. Each round after the first runs inside a step named after the loop and the previous round. For example, a loop named Review and revise that runs two rounds shows round 1’s steps, then a Review and revise: continue after iteration 1 step that holds round 2’s steps. When iterationVariable is set, each round also starts with a step named Review and revise: iteration N, where N is the round number. For more information about reading step results, see Debugging flows.
Design safe loop bodies
Follow these guidelines when you design a loop body:
- Make every round move one shared working variable toward a measurable verdict. Avoid redrafting from the original input on every round.
- Keep
untildeterministic. Have a review or validation step write a boolean or compact verdict, then test that value. - Choose the smallest useful bound. Each round runs every body step again, so the worst case is
maxIterationstimes the body’s model calls, cost, and time. Consider a faster, cheaper model for the review step. - Handle exhaustion explicitly. Reaching
maxIterationscompletes the loop and continues the Flow, but it doesn’t mean the verdict passed. Add a conditional step after the loop with a condition such asreview.verdict !== 'pass'to route that case. - Plan for failed body steps. By default, a failed body step doesn’t stop the round, and the next body step runs. A
tool-callstep is the exception: it stops the loop unless its error handling is set to continue. Writeuntilso it stays false when the review step fails and its verdict variable doesn’t update. When a body step setserrorHandling.onErrortofail, a failure in that step stops the rest of the loop and the loop step reports a failure. - Don’t nest
loopsteps. Put the inner work in the same body, call a separate Flow, or use a conditional step inside the body.
Next steps
- Flow variables and templates: share working context between rounds
- Using conditional steps: route based on the final verdict
- Debugging flows: inspect each round’s step results
- Timeouts and long-running work: use asynchronous handles for runs that may outlive a client request