Zeebe supports FEEL v1.11 – the “Friendly Enough Expression Language” – to express dynamic behavior in your processes.
You can use FEEL expressions in a number of places in your BPMN models.
From the Zeebe documentation:
The following attributes of BPMN elements require an expression:
- Sequence Flow on an Exclusive Gateway: condition
- Message Catch Event / Receive Task: correlation key
- Multi-Instance Activity: input collection, output element
- Input/Output Variable Mappings: source
Additionally, the following attributes of BPMN elements can define an expression optionally instead of a static value:
- Timer Catch Event: timer definition
- Message Catch Event / Receive Task: message name
- Service Task: job type, job retries
- Call Activity: process id
Gateway
The most obvious place that you use an expression in a model is a BPMN Gateway, to test some property of the workflow instance variables, and take a branch depending on its value.
FEEL expressions start with an equals symbol =
, to indicate that it is a FEEL expression that should be evaluated at runtime:
=time.hour >=0 and time.hour <=12
In this example, the FEEL expression examines the hour
subkey of the time
variable. It is a boolean expression using a Boolean AND operation. If the expression returns true, then the token will flow down this branch.
Note: be careful when constructing exclusive gateways to not specify overlapping conditions. If more than one branch evaluates to true, then the token will flow down one of the true branches – but it is unpredictable which it will take.
Safe Access
If you construct an expression that relies on a variable subkey, an incident will be raised if the variable does not exist.
You can test the existence of the variable first using the instance of
operator:
=time instance of context and time.hour >=0 and time.hour <=12
The FEEL engine uses short-circuit evaluation – so if time
is not a context, evaluation of the expression stops.
Zeebe 0.25 will introduce the function is defined
for testing for the existence of a variable.
For a discussion on testing for null in versions of Zeebe prior to 0.25, see this GitHub issue.
Service Task
You can use a FEEL expression for the Type of a Service Task:
=string(tenantId) + "-process-order"
In this example, the FEEL expression dynamically constructs the task type from the variable tenantId
.
FEEL includes a number of built-in functions.
The string
function will convert any variable value to a string value. Here we convert the tenantId
to a string, then concatenate it with -process-order
to get a unique task type.
If the tenantId
variable in a workflow instance is set as 11011011
, then the task type will be 11011011-process-order
.
You could use this, for example, to segment workloads.
If you want to specialize worker behavior based on the value of a variable, you can do that in the worker.
However, if you want segmented workers for QoS, or with isolated access to distinct databases, you can use a FEEL expression to get this dynamic behavior from a single process model.
Timer
Exponential back-off retries is an open feature request for the core engine.
In the meantime, here is how you can use FEEL expressions to model a back-off retry for a task.
In the “Update Retries” worker, you need to update a variable in the workflow with the following structure:
{
retry: {
retries: $number,
timeout: "$duration"
}
}
The worker code should set these values if they do not exist, and update them if they do, decrementing the retries
and appropriately setting the timeout
with the desired delay duration for the next retry (for example “P1D” for a one-day delay).
In the gateway branch leading to the “Back-off Timer”, set the Condition expression to =retry.retries > 0
.
Set the “Back-off Timer” Timer Duration to =retry.timeout
.
Note: if you use this pattern multiple times in the same process, you need to isolate the state – for example by using a unique key name for the retry
key in each instance.
To use the same task type and handler code for the “Update Retries” worker, you could use custom headers to pass in the name of the key to use, and even to specify the back-off strategy to use.
Message
The correlation key of a message catch or receive can be dynamically set.
The correlation key is the name of the variable whose value will be correlated with the message correlation key value.
You can dynamically set the correlation key based on a variable in the workflow instance.
This is similar to indirect addressing in assembly language programming.
If you set the correlation key for a message catch/receive event to =orderType + "Id"
, then with the following workflow variables, the subscription will be opened like this:
{
orderType: "immediate",
immediateId: 435
}
To publish a message to this subscription, you would set the correlation key in the publish message request to 435
.
Summary
These are a few basic examples of using FEEL in Zeebe process models. In the next post, we’ll cover some more advanced uses.
What would you like to see covered in future articles? Let us know via [email protected]. Have a question about Zeebe? Drop by the Slack channel or Zeebe User Forum.

Camunda Developer Community
Join Camunda’s global community of developers sharing code, advice, and meaningful experiences
Start the discussion at forum.camunda.io