Using FEEL for Expressions - Part 1

by Josh Wulf on Sep 8 2020 in Tutorial.

Zeebe supports FEEL v1.11 - the “Friendly Enough Expression Language” - to express dynamic behaviour 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:

Additionally, the following attributes of BPMN elements can define an expression optionally instead of a static value:


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 specialise worker behaviour 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 behaviour from a single process model.


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.


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.


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 Have a question about Zeebe? Drop by the Zeebe Slack channel or Zeebe User Forum.