Skip to main content
JEXL expressions read configured field values and return a value for a specific configuration setting. The JEXL reference lists every registered macro, its arguments, and the expression locations that accept it. In the app, open Configuration > Data Model > JEXL Reference to search the same catalog.

Choose the expression location

The setting determines which fields and named roots an expression can read, which macros it can call, and what happens if it returns null or fails. The expression locations table lists every supported setting. The main groups are: For most entity expressions, use bare field reference IDs such as annualRevenue. Calculated values also offer me, parent, and root according to the containing object. Conversion rules use source.<field> and transaction.<field>. Policy entity invariants can read transaction and policyVersions; other entity invariants cannot. Smart tags offer those version roots only for Quote form types. Policy-row exports offer them; other exports do not. The setting’s own entry in the reference lists its available roots.

Write conditions and calculations

Use =, ==, or != for equality; >, >=, <, and <= for ordered comparisons. Arithmetic uses +, -, *, /, and %. Text and list comparisons are case-sensitive. Parentheses group expressions. ! negates a Boolean. The type checker rejects equality comparisons on Boolean fields and whole lists. Use a Boolean field directly, such as isActive or !isActive. For lists, use membership, a bracket filter, or COUNT. Ordered comparisons require Numbers; use a date function such as DAYS_BETWEEN before comparing dates numerically.
&& binds more tightly than ||, and both short-circuit. The expression a || b && c means a || (b && c). A ternary selects only its chosen branch:
Wrap a function-call condition in parentheses before ?. ?? selects the right value when the left is null or undefined, but its right operand still evaluates. Do not put a sequence allocator on its right side. Use a ternary when the fallback has side effects:
This example assumes a configured reference-number sequence and a Stamped field that accepts sequence allocation. Check the macro’s accepted locations before using it. Use IS_PRESENT(value) for an unset value. It returns false for null, undefined, empty text, and whitespace-only text. Zero and false are present. CLEAR() produces an explicit null, which is useful when a conversion must clear a destination. Do not use a bare null value in an expression; this JEXL grammar does not offer a general null literal. The record-presence comparison transaction.before == null is a narrow supported exception. CONTAINS reads left to right. tags CONTAINS 'urgent' tests list membership; name CONTAINS 'Corp' tests a text substring. IN tests a scalar element in a list, such as 'urgent' IN tags. The engine also has string-to-string IN behavior, but the configuration type checker requires a list on the right. Use CONTAINS for text. Use items[.active] or items[.state = 'CA'] to filter a list. Use items|map('state') to extract one property from each item. map is the registered pipe transform; filter is not. List indexing uses items[0]. Test for presence before depending on an optional element. Group a membership test before negating it. ! binds more tightly than IN or CONTAINS, so !('GL' IN coverageTypes) means the list lacks GL, and !(name CONTAINS 'Corp') means the name lacks that substring. To write “if A, then B,” use !(A) || (B). Date fields store {date: 'YYYY-MM-DD', timezone}. Use YEAR(date), MONTH(date), and DAY(date) to read parts of a date, and ADD_DAYS, ADD_MONTHS, or ADD_YEARS for arithmetic. Wrap a negative literal passed to a function, as in ADD_DAYS(effectiveDate, (-1)). When a Date field needs a Date object from an ISO string, use DATE_VALUE(effectiveDateText, 'America/New_York'); in a conversion, read the source value as source.effectiveDateText. Use FORMAT_DATE only for display text. TODAY() is temporal and is accepted only in the locations listed in the reference.

Author an entity invariant

An invariant is one row of entityInvariants. This example requires a nonempty Policy number:
This row is part of the complete configuration document, not a complete import body. On an entity write, only a strict Boolean true passes. false or null refuses the write with the configured errorMessage. An evaluation error also refuses the write and reports that the named invariant could not be checked. The write does not persist a partially valid entity. Each invariant sees the fields of its declared entity; Policy invariants can additionally inspect the transaction and policy version grid. Bind dry runs apply the same fail-closed rule before Bind. An entity invariant runs once for the entity. It does not automatically run once per item in an ObjectList. To require at least one matching row, use COUNT(rows[.active]) > 0. To require every row to pass a condition, count violations: COUNT(rows[!.active]) = 0. A form placement’s display or required condition changes that placement in the browser; use an entity invariant for a rule that must hold on every write, including API writes. Policy version roots can be absent when a write has no transaction in view, including a candidate data validation run. Guard a rule that depends on the prior policy with transaction.before == null || <condition>. That comparison is the supported record-presence use of null; it does not make null a general value literal.

Validate before importing

Configuration validation first checks JSON shape, expression syntax, supported roots and macros, and references and result types where the setting has enough type information. A reported syntax error means the expression did not compile. A rejected macro means its tag is unavailable at that expression location. A reference or type finding names the affected setting or rule. Some display, list, and rating expressions report type findings as warnings; entity invariants, conversions, and calculated values use errors for invalid types. Treat warnings as work to inspect, not proof that the expression will return the intended value. Macro availability is checked in every branch. A short-circuit condition or ternary does not make an otherwise forbidden macro valid. A valid configuration can still disagree with existing records. A data validation run scans stored records against a candidate configuration. Runtime writes evaluate the active expressions using current entity data. A missing value, a database read, or a calculation can still fail there even after static validation succeeds. The result follows the expression location’s null and error behavior. An external write refused by the example invariant returns HTTP 400 with the normal error envelope. The message includes the data field, userMessages contains the configured message, and details identifies the same field:
Search the exported entityInvariants rows for the text in userMessages, then inspect the matching row’s entity and condition. If the message says the invariant could not be checked, inspect its roots, macro availability, and values at evaluation time. The Policy API overview describes the Check Bind Conditions response, which identifies invariant findings before a bind.

Change configuration safely

  1. Export the complete configuration and keep a copy of the current body.
  2. Edit the relevant expression. Check its location, roots, macros, and destination type against the JEXL reference.
  3. Validate the candidate. Correct syntax, forbidden macros, wrong references, and type findings.
  4. Compare the candidate with the current configuration to review its scope.
  5. Start a data validation run for the candidate when stored records could be affected. Poll the run and read its findings.
  6. Exercise the expression on representative data in a test environment, including missing values and both condition branches. Then import the complete configuration.
Keep framework-required rows and their fixed expressions intact. A configuration import applies the full body, so omitting an unrelated section can remove it.