Rule object¶
Rules live in a field's on array. They run in code right after the
field's stage finishes. See Flow rules for the
concepts; this page lists every key, action and operator.
| Key | Type | Description |
|---|---|---|
when |
object or array | Conditions (all must match). Omitted = always. An array is shorthand for {"in": [...]}. |
do |
string | The action, or a field name to jump to. Required. |
to |
string | Target field, with "do": "jump". |
cases, default |
object, string | Branches, with "do": "switch". |
max |
integer | Times a return or jump may fire (default 3, minimum 1). |
at |
string | Path inside the value to test (e.g. "status", "data.items[0].id"), for object values. |
tol |
number | Tolerance for *_expr operators. |
Order of evaluation¶
- After a stage, fields are checked in schema order; each field's rules in array order.
- A rule whose
whenmatches is applied. - Markers (
review,drop) apply and checking continues with the next rule. - The first flow action (
reject,return, jump,switch,end) applies and stops checking — for that field and for the whole stage.
Actions¶
reject¶
Stop the request.
- No further stage runs.
- Fields of the same stage and fields that did not run get their
on_reject(defaultnull); fields from earlier stages keep their values. - Response:
rejected: {"field": "…", "value": …}.
review¶
Flag the field for a human; continue.
- The field is added to
flagged(with a readable reason) and toneeds_review. - Several matching
reviewrules on one field give one flag with the reasons joined.
drop¶
Keep the value in result, hide it from later fields' prompts.
return¶
Start over: every field runs again and sees the previous round's values and
the reason (for *_expr rules, the expected value; for confidence rules, a
note that the answer was uncertain).
Limited by max (default 3); after that the rule is skipped and later rules
apply.
Jump¶
Go to another field. Write the field name, or {"do": "jump", "to": "…"}.
- Forward: fields between here and the target that have not run are
skipped (
null); the run continues from the target. - Backward (or to itself): the target and every field after it (schema order) run again, seeing their previous values.
- Limited by
max.
switch¶
Run one of several fields, chosen by this field's value.
- Values are compared as text:
true/false,3for3.0,null. - The chosen field runs normally (by its
depends_on); the other targets are skipped (null). - No matching case and no
default→ all targets skipped.
end¶
Finish successfully now. Fields that did not run are null (not
on_reject), rejected stays null, and ended is set.
Condition operators¶
All operators in one when must match (AND). For OR, write two rules.
Equality and membership (any type)¶
| Operator | Example | Matches | Does not match |
|---|---|---|---|
eq |
{"eq": "active"} |
"active", " ACTIVE " |
"inactive" |
ne |
{"ne": "active"} |
"lapsed" |
"Active" |
in |
{"in": ["low", "medium"]} |
"low" |
"high" |
not_in |
{"not_in": ["low"]} |
"high" |
"LOW" |
Text is compared trimmed and case-insensitive. Booleans never equal numbers
(true is not 1). {"eq": null} matches null.
Numeric comparison¶
| Operator | Meaning | Example | Matches | Does not match |
|---|---|---|---|---|
lt |
less than | {"lt": 21} |
20 |
21, 30 |
lte |
less than or equal | {"lte": 40} |
40, 12.5 |
40.01 |
gt |
greater than | {"gt": 55} |
56 |
55 |
gte |
greater than or equal | {"gte": 3} |
3, 5 |
2 |
A range is two operators in one when: {"gte": 21, "lte": 55} matches
21–55 inclusive. Non-numbers (text, null, booleans) never match.
Length¶
| Operator | Meaning | Example | Matches | Does not match |
|---|---|---|---|---|
len_eq |
length equals | {"len_eq": 16} |
"3201456789012345" |
"320145678901234" |
len_lt |
shorter than | {"len_lt": 16} |
15 characters | 16 characters |
len_lte, len_gt, len_gte |
≤, >, ≥ | {"len_gt": 0} |
["a"] |
[] |
Length of trimmed text, or number of list items. Other types never match.
Text and lists¶
| Operator | Example | Matches | Does not match |
|---|---|---|---|
contains |
{"contains": "cash"} |
"Paid in CASH" |
"QRIS" |
contains (list) |
{"contains": 500} |
[404, 500] |
[404] |
match |
{"match": "\\d{16}"} |
"3201456789012345" |
"3201-4567", "32014567890123456" |
not_match |
{"not_match": "\\d{16}"} |
"3201-4567" |
"3201456789012345" |
match and not_match use regular expressions and must match the whole
trimmed text. Remember to escape backslashes in JSON ("\\d"). To search
anywhere in the text, wrap the pattern: "(?is).*(approved|released).*".
Cross-field expressions¶
Compare this field's number with a formula over other fields.
| Operator | Matches when | Example |
|---|---|---|
eq_expr |
value = formula (within tol) |
{"eq_expr": "a + b"} |
ne_expr |
value ≠ formula | {"ne_expr": "subtotal - discount + vat", "tol": 1} |
lt_expr, lte_expr |
value < / ≤ formula | {"lt_expr": "api_price * 0.95"} |
gt_expr, gte_expr |
value > / ≥ formula | {"gt_expr": "api_price * 1.05"} |
- Formula syntax as in
compute. tol: absolute difference still treated as equal (default: exact, allowing for rounding noise).lte_expr/gte_expralso treat "withintol" as equal.- Referenced fields must run in the same or an earlier stage (else HTTP 400). They do not need to be dependencies.
- If this value or any referenced value is
nullor not a number, or the formula divides by zero, the condition does not match.
Example: 108.000 with {"ne_expr": "a + b", "tol": 1} and a + b = 108.900
→ matches (they differ by 900). 108.900,4 → does not match.
Confidence¶
Compare the field's confidence (0–1).
| Operator | Example | Matches (confidence) |
|---|---|---|
confidence_lt |
{"confidence_lt": 0.9} |
0.77 |
confidence_lte |
{"confidence_lte": 0.9} |
0.9 |
confidence_gt |
{"confidence_gt": 0.99} |
0.9998 |
confidence_gte |
{"confidence_gte": 0.99} |
0.99 |
The argument must be a number between 0 and 1. Fields without a confidence
(free_form, compute, call) never match; using these operators on a
free_form field is rejected with HTTP 400.
Combining with at¶
For fields whose value is an object (usually call),
at selects the part to test: