AI Rule Engine Docs
Go to App

Expression Language

Expressions compute a value from other values — ctx.price * ctx.qty, coalesce(ctx.region, "US"), len(ctx.name) > 30. They are small, typed, and deterministic, and you can use one anywhere the engine reads a value.

Where you can use an expression

An expression appears in two places:

  • As a value source — a fourth option (alongside Context, Env Var, and Constant) anywhere a value input appears: a condition compare value, a derived fact value, an Add to Context entry, and action inputs.
  • As the left side of a condition clause — a clause offers a Key / Expression toggle, so instead of “Key to evaluate” you can compare a computed value: len(ctx.name) > 30 as a clause on its own.

The editor shows the hint “Read context with ctx.key, variables with $name” and a placeholder like “e.g. ctx.price * ctx.qty”.

Reading data

FormReads
ctx.keyA context value (the canonical form).
context.keyThe same thing — context is an accepted alias for ctx.
ctx.[key with spaces]Bracket form, for keys that are not simple identifiers (spaces, dots, punctuation).
$nameAn environment variable.

No bare identifiers A name always has to say where it comes from. Write ctx.price or $rate, never a bare price — the same qualified ctx.key / $name shorthand the decision table accepts when you reference context in a cell.

Literals

Numbers are written plainly (42, 3.14), strings in double quotes ("gold"), and booleans as true / false.

Operators

GroupOperators
Arithmetic+-*/%+ also concatenates strings.
Comparison==!=<<=>>=
Logicalandornot (aliases &&||!)
GroupingParentheses ( )
ConditionalTernary condition ? whenTrue : whenFalse

Function reference

The function library is fixed (you cannot add your own). Every function takes typed arguments and returns a typed value.

String

FunctionDoesExample
len(x)Length of a string, or number of items in an array.len(ctx.name)
lower(x)Lowercase a string.lower(ctx.code)
upper(x)Uppercase a string.upper(ctx.code)
trim(x)Remove surrounding whitespace.trim(ctx.input)
contains(x, sub)True if a string contains sub (or an array contains the value).contains(ctx.email, "@")
startsWith(s, prefix)True if s starts with prefix.startsWith(ctx.sku, "AB")
endsWith(s, suffix)True if s ends with suffix.endsWith(ctx.file, ".pdf")
replace(s, find, with)Replace every occurrence of find.replace(ctx.phone, "-", "")
substring(s, start, length?)Slice from start (0-based); length is optional.substring(ctx.code, 0, 3)
concat(a, b, …)Join values into one string; null arguments are skipped.concat(ctx.first, " ", ctx.last)

Math

FunctionDoesExample
abs(n)Absolute value.abs(ctx.delta)
round(n, digits?)Round to digits (default 0).round(ctx.price, 2)
floor(n)Round down to a whole number.floor(ctx.score)
ceil(n)Round up to a whole number.ceil(ctx.score)
min(a, b, …)Smallest of the arguments (or of a single array).min(ctx.a, ctx.b)
max(a, b, …)Largest of the arguments (or of a single array).max(ctx.a, 0)
coalesce(a, b, …)The first argument that is not null.coalesce(ctx.nickname, ctx.name)

Conversion

FunctionDoesExample
number(x)Parse to a number, or null if it cannot.number(ctx.qtyText)
string(x)Format a value as a string.string(ctx.count)
bool(x)Parse to a boolean (true/false, 1/0, yes/no).bool(ctx.flag)
date(x)Parse to a date/time, or null.date(ctx.dob)

Date

FunctionDoesExample
dateAdd(d, amount, unit)Add amount units to a date (day, week, month, year, hour, minute, second).dateAdd(ctx.start, 30, "day")
dateDiff(a, b, unit)a minus b, measured in unit (day, hour, minute, second).dateDiff(ctx.end, ctx.start, "day")
year(d)The year of a date.year(ctx.dob)
month(d)The month of a date (1–12).month(ctx.dob)
day(d)The day of the month.day(ctx.dob)

Array

FunctionDoesExample
count(array)Number of elements.count(ctx.items)
sum(array)Total of the numeric elements.sum(ctx.amounts)
avg(array)Mean of the numeric elements.avg(ctx.scores)
first(array)First element.first(ctx.items)
last(array)Last element.last(ctx.items)
join(array, separator)Join elements into a string.join(ctx.tags, ", ")

Nulls and typing

Null propagates: if any operand is null, an arithmetic or comparison result is null too, and most functions return null when given a null or mistyped argument. The two deliberate exceptions are coalesce (returns the first non-null argument — the escape hatch) and concat (skips null arguments). Numeric comparisons follow the same coercion the engine's conditions use, and the result of an expression is a typed value — string, number, boolean, date, or an array element type — carried through wherever it is used.

Deterministic by design There is deliberately no now() or random(). Expressions depend only on their inputs, so a run is repeatable, an inference rule's re-fire behavior stays stable, and a what-if comparison reproduces exactly. Read the clock or a random seed by putting it in the context as an input instead.

Validation

Expressions are checked as you type: a parse error is reported with the character position of the problem, so you can fix it in place. A malformed expression also fails when you save, so a bad expression never reaches a run.

Worked examples

ctx.price * ctx.qty                        // multiply two context numbers
coalesce(ctx.[customer region], "US")      // default a missing value
len(ctx.name) > 30 and $ENV == "prod"      // a boolean clause expression
ctx.tier == "gold" ? 0.2 : 0.1             // ternary: pick a discount by tier