DMN / Decision Automation

FEEL Expression Language: A Complete Tutorial for DMN

Published 6 May 2026  ·  14 min read  ·  By Priostack Engineering

FEEL — Friendly Enough Expression Language — is the standard expression language defined in the DMN 1.3 specification. Every condition in your decision tables, every output calculation, every hit policy expression is written in FEEL. If you use DMN for business rules — in Camunda, Priostack, or any OMG-compliant engine — you are already using FEEL, even if you did not know the name.

This tutorial covers everything you need to write effective FEEL expressions: the type system, arithmetic, string functions, date handling, list operations, and complete decision table examples you can drop into Priostack today.

What is FEEL?

FEEL was designed with a specific tension in mind: it must be expressive enough for a workflow engine to execute reliably, yet readable enough for a business analyst to write without a developer's help. The "Friendly Enough" in the name acknowledges this compromise.

Key characteristics of FEEL:

Basic Arithmetic

FEEL supports the standard arithmetic operators. Numbers are always arbitrary-precision decimals — there is no integer/float distinction.

ExpressionResultNotes
1 + 23Addition
10 - 37Subtraction
4 * 2.510Multiplication
9 / 42.25Division (exact decimal)
2 ** 8256Exponentiation
floor(3.7)3Round down
ceiling(3.2)4Round up
decimal(3.14159, 2)3.14Round to N decimals
abs(-5)5Absolute value
modulo(17, 5)2Remainder

Comparison and Range Expressions

FEEL's range syntax is one of its most powerful features for decision tables. Instead of writing age >= 18 and age <= 65, you write:

age in [18..65] // inclusive both ends age in (17..65] // exclusive lower bound (>17, <=65) age in [18..65) // exclusive upper bound (>=18, <65) age >= 18 // standard comparison age != 0 // not equal

In decision table input cells you can omit the variable name:

[18..65] // matches if input variable is in this range < 18 // matches if input variable is less than 18 >= 1000 // matches if input variable >= 1000

String Functions

FEEL includes a rich set of built-in string functions:

ExpressionResult
string length("hello")5
upper case("hello")"HELLO"
lower case("WORLD")"world"
substring("BPMN 2.0", 1, 4)"BPMN"
substring after("hello world", "hello ")"world"
string join(["a","b","c"], "-")"a-b-c"
contains("Priostack", "stack")true
starts with("BPMN", "BPM")true
ends with("process.bpmn", ".bpmn")true
matches("abc123", "[a-z]+[0-9]+")true
replace("foo bar", "bar", "baz")"foo baz"
trim(" hello ")"hello"

String concatenation uses the + operator:

"Hello, " + customerName + "!" // "Hello, Alice!"

Date Operations

FEEL has first-class date, time, and duration types. This is critical for business rules involving deadlines, SLAs, and scheduling.

date("2026-05-06") // date literal time("14:30:00") // time literal date and time("2026-05-06T14:30:00") // date-time literal duration("P1Y2M3D") // period: 1 year, 2 months, 3 days duration("PT8H") // duration: 8 hours today() // current date now() // current date-time // Arithmetic on dates date("2026-05-06") + duration("P30D") // 2026-06-05 date("2026-06-05") - date("2026-05-06") // duration P30D // Extracting parts date("2026-05-06").year // 2026 date("2026-05-06").month // 5 date("2026-05-06").day // 6 date("2026-05-06").day of week // "Tuesday" // Comparing dates in decision tables dueDate < today() // overdue check contractEnd - today() < duration("P30D") // expiring soon

List Operations

FEEL lists are ordered sequences of any type. They are particularly useful for multi-value outputs and for checking membership:

["red", "green", "blue"] // list literal count([1,2,3,4]) // 4 sum([10, 20, 30]) // 60 min([5, 2, 8, 1]) // 1 max([5, 2, 8, 1]) // 8 mean([10, 20, 30]) // 20 // Membership "red" in ["red","green","blue"] // true 5 in [1..10] // true // List manipulation append([1,2], 3) // [1,2,3] concatenate([1,2],[3,4]) // [1,2,3,4] reverse([1,2,3]) // [3,2,1] sort([3,1,2], function(x,y) x < y) // [1,2,3] flatten([[1,2],[3,[4,5]]]) // [1,2,3,4,5] distinct values([1,2,2,3]) // [1,2,3] // Filtering (equivalent of WHERE clause) items[item > 100] // filter list by predicate some item in prices satisfies item > 500 // any item over 500? every item in scores satisfies item >= 60 // all passing?

Decision Table Examples

Let us see FEEL in action inside real DMN decision tables. In Priostack, you can deploy DMN tables alongside your BPMN processes and call them from service tasks or gateway conditions.

Example 1: Loan Risk Rating

This decision table assigns a risk category based on credit score and loan amount:

Credit Score Loan Amount (€) Risk Category
>= 750< 50000"LOW"
[600..749][1..30000]"MEDIUM"
[500..599]-"HIGH"
< 500-"REJECT"

The output expression for a calculated interest rate could be:

// FEEL output expression based on riskCategory variable if riskCategory = "LOW" then 0.03 else if riskCategory = "MEDIUM" then 0.07 else if riskCategory = "HIGH" then 0.14 else null

Example 2: Discount Calculation

// Input: orderValue (number), customerTier ("GOLD" | "SILVER" | "STANDARD") // Output: discountPercent (number) if customerTier = "GOLD" and orderValue >= 1000 then 0.20 else if customerTier = "GOLD" then 0.15 else if customerTier = "SILVER" and orderValue >= 500 then 0.10 else if customerTier = "SILVER" then 0.05 else 0.0

Example 3: SLA Deadline Check

// Is a ticket overdue? ticketCreatedAt + duration("PT4H") < now() // Days remaining in contract (contractEndDate - today()).days // Is the request within business hours? (09:00-17:00 weekday) time(now()) >= time("09:00:00") and time(now()) <= time("17:00:00") and day of week(now()) in ["Monday","Tuesday","Wednesday","Thursday","Friday"]

Integration with Priostack DMN

Priostack evaluates FEEL expressions natively — no external rule engine, no scripting sandbox. Your DMN decision tables deploy as part of your process definition and are called synchronously during process execution. Variables from the process context are automatically available as FEEL context entries.

To evaluate a decision table from a BPMN service task, call the Priostack REST API:

POST /api/v1/decisions/evaluate Authorization: Bearer YOUR_API_KEY Content-Type: application/json { "decisionId": "loanRiskRating", "variables": { "creditScore": 680, "loanAmount": 25000 } }

Response:

{ "result": { "riskCategory": "MEDIUM" }, "matchedRules": [2] }

You can also call decisions inline from within BPMN by adding a business rule task with the decision reference set to your DMN decision key. No extra code needed.

For interactive FEEL expression testing and the full function reference, see the Priostack FEEL documentation.

Start using DMN and FEEL in your workflows today

Deploy your first decision table in minutes with a free Priostack account. No infrastructure required.

FEEL docs Quickstart guide

Frequently Asked Questions

What is FEEL in DMN?

FEEL stands for Friendly Enough Expression Language. It is the expression language defined in the DMN 1.3 specification, used to write input conditions and output values in decision tables. FEEL is designed to be readable by business analysts while being precise enough for execution engines.

Is FEEL case-sensitive?

Yes. FEEL is case-sensitive. Variable names, function names, and keywords must match exactly. true and false are lowercase boolean literals. Built-in functions like string length use lowercase with spaces.

What is the difference between FEEL and JUEL?

FEEL is the OMG-standard expression language for DMN decision tables. JUEL (Java Unified Expression Language) is a Java-specific EL used in older Camunda 7 versions. Priostack uses FEEL 1.3, which is portable and standards-based.

Can FEEL expressions access process variables?

Yes. When a DMN decision is evaluated from within a BPMN process, process variables are available as FEEL context variables by name. If your process has a variable customerAge, use it directly: customerAge >= 18.

How do I test FEEL expressions without deploying a full process?

The Priostack FEEL docs page includes an interactive evaluator. You can also evaluate expressions via the REST API: POST /api/v1/feel/evaluate with the expression and context variables as JSON.

Related: ArchiMate layers and enterprise modeling  ·  Two-layer BPMN architecture  ·  Full FEEL language reference