Rules: three shapes, not two
/api/rest/rules is a general-purpose object for account-specific if-this-then-that policies (see Rule in the glossary), but the fields you populate depend on the rule’s type — mixing them up produces a rule that silently never fires.
- Action-only — no condition fields at all: only
actionType + actionValue are set, and the rule’s type itself determines when it applies. This is the most common shape in production (e.g. ON_CREATION_FROM_EMAIL rules) — don’t assume every rule needs a condition or a DSL expression.
- DSL expression pair — used by
type: "CUSTOMER_INVOICE_EXCEPTION" rules: conditionExpression (when the rule applies — an expression like Shipment.mode = 'FTL' AND Invoice.totalAmount > 1000, or the literals ALWAYS / NEVER) and validationExpression (what to check — REQUIRE Document WHERE ... or VALIDATE ... = ..., see the worked example below). For CUSTOMER_INVOICE_EXCEPTION rules, send both DSL fields. A check constraint (check_rule_dsl_format) enforces it for DSL-driven rules. The exception is config-managed exception types — ones evaluated in application code rather than by the DSL, which are allowed to carry NULL expressions and act as on/off toggles. That exempt set has been widened several times, so treat it as a moving list rather than a fixed one: if you are building a config-managed toggle row, check the current constraint instead of assuming a value is or isn’t exempt. For anything DSL-driven, both fields are required.
- Structured criteria (
criteriaObject / criteriaField / criteriaOperator / criteriaValue) exist on the table but have no live usage today — treat this as legacy/reserved rather than a mechanism to build against.
Other fields that apply regardless of mechanism:
Rules don’t soft-delete (no deletedAt) — DELETE /api/rest/rules/{id} is permanent. If you might want the rule back later, PUT it with enabled: false instead of deleting it.
Example: validate a customer invoice against its customer rate confirmation
Once customer rate confirmations are flowing in, a shipper rule (a CUSTOMER_INVOICE_EXCEPTION-type rule using the DSL expression pair) can hold a customer invoice for review when its amount doesn’t match the customer’s rate confirmation. The rule compares the parsed customer rate confirmation total against the shipment’s customer total rate — both customer-side amounts, stored in cents:
When the amounts differ, the invoice opens a CUSTOMER_INVOICE_EXCEPTION and stays in Needs Review instead of going out to the customer. The comparison only ever reads CUSTOMER_RATE_CONFIRMATION documents — it never compares against a CARRIER_RATE_CONFIRMATION.