Looping in ProcessFlow
Document information
- Canonical URL:
/docs/02_automations-and-processflow/16_looping-in-processflow - Version:
2026-05-13 - Tags:
processflow,loops,iteration,guides
ProcessFlow runs steps in order according to your process definition. There is no separate “loop” step type: repetition is expressed either inside a step (your snippet walks a bounded collection) or across executions by starting another run of the same process when more work remains.
This page describes those patterns and how to keep them efficient.
Repeat work inside one step (same execution)
When all items for a pass are already available in process_input or can be read in one go (for example from tenant storage or a prior step’s output), the usual approach is to iterate in step code: validate input, loop over an array or page of records, build a result, and return a single structured payload for the next step.
Best practices for performance
- Keep each step’s workload predictable: prefer fixed-size batches (for example process at most N rows per run) instead of unbounded lists in one execution.
- Return only the fields the next step needs, so payloads stay small and serialization stays fast.
- If work does not fit comfortably in one execution, split the batch and use the cross-execution pattern below instead of growing a single step’s runtime and memory use.
Use the process step code guide and sandbox guardrails for what step code may call and how returns should be shaped.
Continue work across runs: async execute-process on the same process
When the next chunk of work should run after the current process execution finishes (or without blocking the current step), a supported approach is to call the ProcessFlow process execution API with async: true, passing the same process_id as the process you are in, and a fresh input_data payload that carries your cursor (page offset, last processed id, remaining count, and so on).
From inside a process step, use the injected internal client so tenant and execution context are applied consistently:
- TypeScript:
await api.post('/api/v1/processflow?action=execute-process', …) - JavaScript / TypeScript:
api.post('/api/v1/processflow?action=execute-process', …)
Callers outside a running process (for example automation scripts) authenticate with your tenant API key and headers as described in ProcessFlow API documentation and asynchronous processing.
Each queued run is a new process execution: it starts from the beginning of the process definition with the input_data you supplied. Design early steps to stay light when input_data indicates a continuation (for example skip no-op validation or short-circuit when a mode flag is continue_batch), so continuation runs do not repeat expensive work unnecessarily.
Best practices for performance
- Prefer larger batches per execution when the same outcome can be achieved with fewer cross-process calls (each
execute-processschedules a full process run). - Put explicit stop rules in
input_data(for exampledone: true,remaining: 0, or “no next page”) so the last run does not enqueue another. - Pass a compact cursor (ids, offsets, hashes) rather than resending large payloads on every continuation.
- Use
async: truefor self-scheduled continuations so the current step can return promptly; use synchronous execution only when the caller must wait for the full chain in one request. - When you rely on repeated
execute-processruns, put the iterated work in its own process so one-off phases (for example initial approval or global setup) live in a parent or caller process and are not repeated on every continuation. - Align priority and operational monitoring with ProcessFlow triggers and your queue habits so high-volume continuations remain observable.
Choosing between the two patterns
| Situation | Favour |
|---|---|
| All records for the pass are small and available in one execution | Loop inside one step (or a small number of steps) |
| Work must pause between chunks, or one execution should not hold everything | Async execute-process with the same process_id and updated input_data |