Skip to main content

Rules Step

The Rules step runs a rule set and stores the values it produced. Where a Condition step decides what runs next, a Rules step decides what a value is — a price, a band, a discount, an eligibility flag — and leaves the routing to somebody else. This page covers the step: its payload, where its inputs come from, and what it puts into the run. For the rule language itself — the operator set, nesting, and how a rule is evaluated — see the Rules reference.

The payload

object
required
A map of fact name → rule. Each key is a value the step will produce; each value describes how to produce it.
object
Starting values the rules can read. This is how data from the run gets in.
object
Evaluation context. timezone is the field the date operators use.
A rule — the value side of a rules entry — is one of:

Where the facts come from

The whole payload is JSONPath-resolved before the engine runs, so values from earlier steps arrive through facts:
With 80 seats on the team plan this produces discount.value of 0.15 and tier of "priority".
@fact: works here because a Rules step passes your facts object to the engine. It does not work in a Condition step, which is called with an empty fact set — inline the value with a $. path there instead.

What the step stores

The step’s result is a flat map of fact name to outcome. The engine’s {outcome: …} wrapper is stripped before the value reaches the flow:
so you read it as:
A fact name containing a dot — the .value convention does exactly this — is one key, not a path. Read it with bracket notation, $.PRICING["discount.value"], not $.PRICING.discount.value.There is no .outcome to append. $.PRICING.tier.outcome resolves to nothing.
outcomeMessage on a winning cell is likewise discarded by this step. If you need the explanation downstream, make it a fact of its own.

The .defaultValue convention

A rule key ending .defaultValue fills in the matching .value key when nothing else has supplied one:
produces both shipping.defaultValue and shipping.value set to 0. Supply shipping.value as a fact, or write a rule for it, and the default stands aside.

When a value does not appear

Two different things can be wrong, and they announce themselves very differently. An operator the engine does not have fails the step, before any rule runs. The message names the rule and the operator:
The same check catches an empty operator, a condition that is not an {operator, input} object, and any string starting with @ whose first segment is not an operator name — which is why an @ you meant as a literal has to be written [@]. Everything else is silent. A ladder where no cell matched and there is no default, a calculation that produced NaN or Infinity, an outcome of null, an outcome that happens to equal the fact of the same name you supplied — each drops its key from the result with no error and no warning. The run continues, the key is absent, and every step downstream reads an empty array where it expected a number.
Give any rule that must always produce a value a final cell with no condition, or a .defaultValue sibling. That converts the whole silent family into a value you can see.
The Rules tab in the editor also validates the payload against a schema as you type, which catches a malformed rule while you are writing it.

Rules compared with the alternatives

Rules earns its place when the logic is a policy someone non-technical needs to read and change. For anything that would be one line of JavaScript, Eval is shorter and fails loudly on every mistake, not just a misnamed operator.
Editing a flow changes its draft. Publish to make a rule change take effect.

Next Steps

Rules Reference

The rule language in full

Operator Reference

Every operator the engine has

Condition Step

Branch on what the rules produced

Eval Step

The same job in JavaScript