Skip to main content
Flow control shapes how a workflow runs under load. Each control is an optional, flat field on the workflow definition, alongside retry. A workflow with no flow config runs unshaped - every control is opt-in and off by default.

At a glance

Keys

Most controls take an optional key: a dotted path into the event data that scopes the control to a value. key: "accountId" gives each account its own independent limit; key: "user.id" reads a nested field. Keys are field paths, not expressions. An omitted key - or a missing / non-scalar field - scopes the control to the whole workflow.

Concurrency

Caps how many runs execute at once in a scope. A slot is held only while a run is actively executing, so a run that is sleeping or awaiting an event releases its slot and does not consume one. The count is taken across the shared database, so the limit is global, not per-process. Over-limit runs are not dropped - they wait and retry as slots free, preserving order.

Project-wide ceiling

Above the per-workflow concurrency you set in code, each project has a concurrency ceiling that caps how many runs execute at once across the whole project, regardless of workflow or key. It comes from your plan, not from a workflow field (0 = unlimited). A run must clear both its per-workflow limit and the project ceiling to start; whichever is tighter applies, and an over-ceiling run waits and retries exactly like a per-workflow over-limit run.

Throttle

Bounds how often runs start, smoothing bursts by spreading overflow into the future - one start every perMs / limit. No run is lost; excess runs begin later.

Rate limit

Same window as throttle, opposite action: instead of delaying overflow it drops it. Up to limit runs start per perMs; the rest are shed and the event response reports dropped: true. Use it for abuse protection where shedding beats queueing.
Throttle and rate limit share one rate primitive (GCRA). Throttle delays the overflow; rate limit drops it.

Debounce

Coalesces a burst of events into a single run that fires after periodMs of quiet. Each new event slides the deadline forward and replaces the payload, so only the last event in a quiet-bounded burst runs.

Batch

Collects events into one run, flushing when the buffer hits maxSize or timeoutMs elapses, whichever comes first. The run receives the events as ctx.events; ctx.event is the first of them.

Priority

Shifts a workflow’s runs earlier in the shared queue by shiftMs, so they dequeue ahead of other workflows competing for the same slots.

Singleton

Allows at most one non-terminal run per key.

Idempotency

Suppresses a second run of this workflow for the same derived key within a time window. The first matching event starts a run; a later event whose key resolves to the same value inside the window is dropped for this workflow (the response reports deduped: true with no runId). The event is still recorded and still wakes waitForEvent waiters - only the duplicate run is suppressed.
The key is a dotted field path into the event data, resolved the same way as every other control. A structurally malformed path - empty segments, or a leading or trailing dot - is rejected when the workflow is registered.
When the path names a field the event does not carry, or the value at it is not a scalar (string, number, or boolean), the key resolves to the shared workflow-wide window - so a mistyped path silently stops per-key deduplication and folds distinct events into a single window.
To verify a per-key path resolves, send two events with distinct payloads and confirm two runs start. Sending the same payload twice is deduped whether or not the path resolves, so it proves nothing. This is run-level dedupe keyed off the event payload. To dedupe a whole event regardless of which workflows it matches - the usual safety net for an at-least-once caller retrying POST /events - send a dedupeId on the event instead (see Events); a repeat of that id within 24h is dropped before any fan-out.

AI spend controls

Two more controls are declared the same way but meter AI spend rather than run starts. They are documented with the rest of the spend tooling in Cost controls; the table is the short version.

Observing flow control

The console renders the same three reads in the Workflows list and on Overview.