Skip to content

Conditions

A step with when runs only when its condition holds for the request payload; otherwise it is skipped and the skip is recorded. Conditions are plain JSON, so a flow can be stored, compared and shown to people, and no code from a flow ever runs.

// Plain JSON, evaluated against the payload; no code runs.
const bigOrForeign: Condition = {
any: [
{ field: "amount", op: "gt", value: 10_000 },
{ field: "vendor.country", op: "nin", value: ["NL", "BE", "DE"] },
],
};
const notInternal: Condition = { not: { field: "internal", op: "eq", value: true } };

Runs in CI:examples/docs-snippets/src/concepts/conditions.ts

Operator Compares
eq, neq Any JSON value, strictly (1 is not "1")
gt, gte, lt, lte Numbers with numbers, strings with strings (ISO timestamps compare correctly)
in, nin A value against a list
all, any, not Other conditions

field is a dot path into the payload (vendor.country, lines.0.amount).

A comparison that does not make sense, such as a number against a string or a missing field, never quietly counts as false. Rungs evaluates every condition at submit, so the requester gets the error, not an approver weeks later:

try {
await rungs.submit({
flow: "purchase",
requesterId: "u_req",
subject: { type: "purchase", id: "PO-9" },
payload: { amount: "12000", vendor: { country: "US" }, internal: false }, // amount is a string
});
} catch (error) {
if (error instanceof ConditionError) {
// Refused at submit, so an approver never meets a broken condition later (EC-40, EC-41).
console.log(error.field, error.expected, error.received); // "amount" "number" "string"
refused = error;
}
}

Runs in CI:examples/docs-snippets/src/concepts/conditions.ts

Conditions read the payload as it was at submit (EC-36). When the data changes, resubmit the request with the new payload.