Using FEEL for Expressions – Part 1

  • Blog
  • >
  • Using FEEL for Expressions – Part 1

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


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.

gateway condition expressions

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:

task type expression

=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.


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.

backoff retry

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 [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

Try All Features of Camunda

Related Content

There are good reasons BPMN been so popular for so long. Let's talk about what BPMN can really do and why it's brilliant.
Visualization leads to optimization—workflow diagrams give you a clear map of even the most complex processes.
We answer all your questions from our most recent webinar on how to "Adapt Faster with Camunda," covering AI, end-to-end orchestration, and more.