FEEL Reference
FEEL (Friendly Enough Expression Language) is the standard expression language defined by the OMG for use in DMN and BPMN. Expressions in Priostack - sequence-flow conditions, DMN input expressions and output entries, CMMN sentries - use FEEL.
The engine implements a subset of FEEL, and this page lists what it evaluates. An expression outside that subset has no answer: in a gateway condition the instance goes to INCIDENT, and in a DMN cell the deploy refuses it.
POST /api/v1/decisions/{decisionId}/evaluate, or in a gateway condition, by starting an instance with sample variables and reading where it went.
Types
| Type | Example | Notes |
|---|---|---|
number | 42, 3.14, -7 | 64-bit floating point. All numeric literals are numbers. |
string | "hello", "world" | Double-quoted. Supports Unicode. |
boolean | true, false | Case-sensitive lowercase. |
date | date("2026-05-01") | ISO 8601 date. Use today() for current date. |
time | time("14:30:00") | ISO 8601 time. |
date and time | date and time("2026-05-01T14:30:00") | Combined date-time value. |
duration | duration("P1D"), duration("PT2H30M") | ISO 8601 duration. P=period, T=time part. |
list | [1, 2, 3], ["a", "b"] | Ordered collection. One-based indexing. |
context | { name: "Alice", age: 30 } | Key-value map. Access with dot notation: person.name. |
null | null | Absence of a value. null equals only null; x != null is true only when x has a value; <, <=, >, >= with null are false. In a gateway condition a variable that was never set is an error (incident), so initialise it, to null if need be, before the gateway. |
Arithmetic Operators
// Basic arithmetic
1 + 2 // => 3
10 - 3 // => 7
4 * 5 // => 20
10 / 3 // => 3.3333...
// there is no exponent operator: ** is not evaluated
// With variables
amount * 1.2 // apply 20% markup
(price - cost) / cost * 100 // percentage margin
// Duration arithmetic
date("2026-05-01") + duration("P30D") // => date("2026-05-31")
now() - duration("PT1H") // one hour ago
Comparison Operators
amount = 100 // equal
amount != 100 // not equal
amount < 100 // less than
amount <= 100 // less than or equal
amount > 100 // greater than
amount >= 100 // greater than or equal
// Range expressions (shorthand for comparisons)
amount in [0..100] // 0 <= amount <= 100 (inclusive)
amount in ]0..100[ // 0 < amount < 100 (exclusive)
amount in [0..100) // 0 <= amount < 100 (mixed)
// Membership test
status in ("pending", "review", "approved") // any of these values
A range opening with (, such as (0..100), and a square-bracket list, such as ["pending", "review"], are not accepted after in today: write an exclusive bound with ] or [ turned outward, and a list of values in parentheses.
Logical Operators
true and false // => false
true or false // => true
not(true) // => false
// Compound conditions
amount > 0 and status = "approved"
(tier = "gold" or tier = "platinum") and amount > 100
String Functions
| Function | Example | Result |
|---|---|---|
string length(s) | string length("hello") | 5 |
substring(s, start) | substring("hello world", 7) | "world" |
substring(s, start, len) | substring("hello world", 1, 5) | "hello" |
contains(s, match) | contains("hello", "ell") | true |
starts with(s, prefix) | starts with("hello", "hel") | true |
ends with(s, suffix) | ends with("hello", "llo") | true |
matches(s, pattern) | matches("hello", "hel+") | true |
upper case(s) | upper case("hello") | "HELLO" |
lower case(s) | lower case("HELLO") | "hello" |
string(x) | string(123) | "123" |
number(s) | number("12") | 12 |
Not implemented: trim, string join, split.
Date and Time Functions
| Function | Description |
|---|---|
now() | Current date and time |
today() | Current date (without time) |
date(s), time(s), date and time(s), duration(s) | Build a value from an ISO 8601 string |
years and months duration(from, to) | Duration between two dates in years and months |
.year .month .day .weekday .hour .minute .second | Properties of a date or date-time; weekday is a number, Monday = 1 to Sunday = 7 |
.years .months .days .hours .minutes .seconds | Properties of a duration |
Not implemented: day of week, day of year, month of year, week of year.
// Check if a date is at the weekend (d holds a date such as "2026-10-03")
d.weekday >= 6 // => true on Saturday and Sunday
// Calculate age from birthdate, as a value
years and months duration(date("1990-01-15"), today()).years // => age in years
// Check if overdue
dueDate < today() // => true if past due
Compare a property read from a variable, as in d.weekday >= 6. A property read directly off a function call works as a value, but comparing it does not work today: years and months duration(date(birth), today()).years >= 18 answers false, and has no answer in a gateway condition.
List Functions
| Function | Example | Result |
|---|---|---|
count(list) | count([1, 2, 3]) | 3 |
sum(list) | sum([1, 2, 3]) | 6 |
min(list) | min([3, 1, 2]) | 1 |
max(list) | max([3, 1, 2]) | 3 |
list contains(list, item) | list contains(["a","b"], "b") | true |
Not implemented: append, concatenate, insert before, remove, reverse, index of, distinct values, flatten, sort.
Filter and Quantifier Expressions
// Filter: keep items matching a condition
items[price > 100] // all items where price > 100
items[status = "approved"] // all approved items
// Some/Every quantifiers
some x in items satisfies x.price > 100 // true if any item has price > 100
every x in items satisfies x.valid = true // true if all items are valid
// For expression: transform a list variable
for item in items return item.name // list of names
// iterating over a range (for i in 1..5) is not supported
// Access first element
items[1].name // name of first item (1-based)
items[-1].name // name of last item
Context Operations
// Create a context
{ name: "Alice", age: 30, address: { city: "Paris" } }
// Access fields
person.name // => "Alice"
person.address.city // => "Paris"
// Not implemented: context merge, context(), get entries, get value
Conditional Expressions
// if/then/else
if amount > 1000 then "high" else "low"
// Combine conditions instead of nesting: an if inside another if is not
// supported. Use a decision table for more than two branches.
if tier = "gold" and amount > 500 then 15 else 0
// Guard against a missing value: customer.email when customer holds a value, null otherwise
if customer != null then customer.email else null
Function Definitions
Not supported: the engine evaluates neither function literals nor functions defined inside a context. Put reusable logic in a decision table, or compute it in a worker.
Numeric Functions
| Function | Example | Returns |
|---|---|---|
abs(n) | abs(-5) | 5 |
floor(n) | floor(3.7) | 3 |
ceiling(n) | ceiling(3.2) | 4 |
min(list) | min([1,5,3]) | 1 |
max(list) | max([1,5,3]) | 5 |
sum(list) | sum([1,2,3]) | 6 |
min, max and sum also take separate arguments: sum(1, 2, 3). Not implemented: round up, round down, decimal, product, mean, median.
Usage in DMN Decision Tables
In decision table input cells, FEEL expressions are used as unary tests against the input column value:
| Input cell | Matches when input value… |
|---|---|
"LOW" | equals the string "LOW" |
< 600 | is less than 600 |
[600..750] | is between 600 and 750 (inclusive) |
"LOW","MEDIUM" | is either "LOW" or "MEDIUM" |
not("HIGH") | is anything except "HIGH" |
| (empty) | matches any value (wildcard) |
Output cells contain FEEL expressions, within the subset above:
// Output: fixed string "approved" // Output: conditional based on other variables if amount > 100000 then "escalate" else "approve" // Output: computed value amount * interestRate
Usage in BPMN Gateways
In BPMN sequence flows from exclusive gateways, add a <conditionExpression>:
<sequenceFlow id="to-approve" sourceRef="gw1" targetRef="approve-task"> <conditionExpression>= riskLevel = "LOW" or (riskLevel = "MEDIUM" and amount < 50000)</conditionExpression> </sequenceFlow>
< for < inside the XML, as above: a raw < is not XML and the deploy answers 422. The leading = is optional: the engine reads amount > 1000 and =amount > 1000 the same way. Writing it keeps the model compatible with Camunda Modeler.