Skip to Content
Flower 3.7.0 is released 🎉 See what's new
Developer Reference· 9 min read· Updated November 2026

Expressions

Flower evaluates expressions on the outgoing sequence flows of exclusive and inclusive gateways and in conditional events. The guide Decide with Gateways shows how to use them; this page is the complete reference.

Syntax

  • An expression is a condition in a JavaScript-like syntax inside ${ }, for example ${cur.fields.status.name == 'Rejected'}. Flower evaluates it with its own parser, which runs no code; what an expression can contain is listed in What an Expression Can Do.
  • Without the braces, Flower adds them: cur.fields.status.name == 'Rejected' is the same condition.
  • Write the whole condition inside one pair of braces. In ${cur.fields.status.name} == 'Rejected', only the part in the braces is evaluated, and the result is a text such as Done == 'Rejected', which counts as true whatever the status is.
  • Names are case-sensitive: summary, not Summary. Compare strings with strings and numbers with numbers.
  • The names of errors, escalations, messages, and signals in a model (for example errorCode="Cancelled") are plain text, not expressions, unless they contain ${ }.
  • A ${ without its closing } is an error (the condition is false), and so is an expression longer than 2000 characters.
  • A script condition (a condition with a script language, from older models) is not executed: the condition is always false, the process instance gets the error comment script conditions are not executed, use an expression, and the model validator warns. Change it to an expression.
  • Timers can be an expression too, for example ${pi.fields.customfield_10050}: it is evaluated first, and its value must be a duration, a date, or a number of days. An empty value means the timer is not set. See Define the Timer.

What an Expression Can Do

PartAllowed
ValuesTexts ('text', "text", with the escapes of JavaScript such as \n, \u00fc, \x41), numbers (12, 1.5), true, false, null, undefined, lists (['High', 'Highest'])
Variablespi, cur, par, model, and nothing else
Fieldspi.fields.summary, pi.fields.labels[0], cur.fields['customfield_10050'], a?.b; a negative list index counts from the end: pi.fields.labels[-1] is the last label
Operators== != === !== < > <= >= && || ! ?? + - * / % ?: typeof, parentheses
Methods of a textincludes, startsWith, endsWith, indexOf, lastIndexOf, toLowerCase, toUpperCase, trim, trimStart, trimEnd, split, slice, substring, substr, charAt, at, concat, replace and replaceAll (with texts), padStart, padEnd, repeat, toString
Methods of a listincludes, indexOf, lastIndexOf, join, slice, concat, at, toString
Methods of a numbertoFixed, toString
FunctionsNumber, String, Boolean, parseInt, parseFloat, isNaN, Array.isArray, Math.min, Math.max, Math.abs, Math.round, Math.floor, Math.ceil
Length.length of a text or a list

Not allowed: arrow functions and the list methods that need them (some, filter, map, …), =, new, template literals, Date, JSON, Object, the other functions of Math, regular expressions, any other function or method, and properties that an object only inherits (such as constructor). Such a condition is false, and the process instance gets an error comment that names what is not allowed (Expression could not be evaluated at …: Not allowed: Date.now(), once per process instance). The model validator reports it already when the model is published.

Differences to JavaScript: two objects or lists are equal only if they are the same one (pi.fields.priority == cur.fields.priority compares two separate objects and is false; compare .name or .id). A text an expression builds is limited to 100000 characters, a list to 10000 items.

Examples: ${pi.fields.labels.includes('approved')}, ${['High', 'Highest'].includes(cur.fields.priority.name)}, ${Number(pi.fields.customfield_10113) > 5000}, ${cur.fields.summary.toLowerCase().startsWith('urgent')}, ${pi.fields.customfield_10113 >= 5000 && cur.fields.priority.name != 'Low'}, ${pi.fields.labels.length > 0 ? pi.fields.labels[0] == 'vip' : false}.

Variables

VariableContains
piThe process instance. Its fields are under pi.fields and also directly on pi (pi.summary is pi.fields.summary, in every run of the engine), its work item properties under pi.properties, plus pi.key and pi.id
curThe current work item: the work item whose change moved the process to this point, see What cur Is. Its fields are under cur.fields and also directly on cur (cur.summary), in every run
parThe calling process instance in a sub-process (call activity). Outside a sub-process it is empty
modelThe model work item. Not set in the run that starts a process instance (up to its first wait); set in every later run

Each work item is the JSON that the Jira REST API returns for it: fields and properties.

What cur Is

cur is the work item whose change made Flower run the process. In detail:

Situationcur
A work item of a task is resolvedThat work item
After a parallel or inclusive joinThe work item whose resolution completed the join, that is the branch that was resolved last
A conditional boundary eventThe resolved work item of the activity the event is attached to
After a call activityThe sub-process instance (the work item of the call activity)
An escalation (status change of an open work item)The escalated work item
An intermediate or start conditional event firesThe process instance, because its change was checked
After the timer jobThe work item of the timer (boundary timer on a task) or the process instance (intermediate timer, timer start event, timer on a sub-process), loaded with all its fields. If several timers of the instance are due, the one the job found first: a work item with a due boundary timer before the process instance
Right after the start of a process instanceEmpty: a condition on cur there is an unknown field (see below). Use pi
Inside an embedded sub-processThe same as in the main process

Work items that the REST API creates with POST …/activities are created without the engine, so no expression is evaluated.

When an Expression Fails

ProblemResultComment on the process instance
A field that does not exist (a typo, a field that the work type does not have, cur right after the start)The missing value counts as empty: ${pi.fields.typo} alone is false, but a comparison such as ${pi.fields.typo != 'x'} is true. Only a result that is falsy makes the condition falseAn error comment Expression validation failed at … with the expression, the missing field, and the keys of pi, cur, and model. Once per process instance for each element and expression, not with every change: Flower remembers the last 50 reported ones in flower.reportedExpressions
A field that exists but is empty, also a property of an empty field (${pi.fields.customfield_10115.value == 'yes'} with the field empty)The condition is falseNone
An expression that cannot be parsed, or that uses something not allowedThe condition is falseAn error comment Expression could not be evaluated at … with the parser message, once per process instance
No outgoing flow of a gateway is true and there is no default flowThe process does not go onAn error comment The gateway … found no outgoing flow whose condition is true …, see BPMN Gateways
par is used, but the calling process instance cannot be loadedAn error comment Could not load the calling process instance …, the expression object par is empty

Example of the comment for a missing field:

❌ Expression validation failed at: bpmn:ExclusiveGateway[Gateway_10td181] Expression: ${cur.fields.summary.value == 'a'} Error: - Missing field 'value' at path 'cur.fields.summary.value' Context: pi: FLOW-106 cur: FLOW-107 model: FLOW-47 🔍 Tip: Use the Jira REST API to inspect your issue data: → /rest/api/latest/issue/your-issue-key 📘 Docs: https://flower-bpm.com/docs/concepts/gateway-decision-handling

In Jira Data Center, Flower also writes the evaluation to its log file, see Logging.

Complex Fields

Not every field is as simple as the summary. Many fields, especially custom fields, are objects. The request type of a service space work item is an example.

Jira Custom Fields in General

Custom fields in Jira have a type, and most types are objects and not simple values. Many have a simple string in .value:

${pi.fields.customfield_1234.value == 'yes'}

Other fields have a more complex structure. To write the right path, look at the JSON of your work item.

Find the Structure of a Field

Custom field IDs differ between Jira instances. For example, the request type is stored in a custom field with a different ID in each instance.

To see the structure, open the work item in the Jira REST API. Open this URL in your browser while you are logged in to Jira:

https://your-domain.atlassian.net/rest/api/3/issue/FLOWER-123

Or use curl:

curl --request GET \ --url 'https://your-domain.atlassian.net/rest/api/3/issue/FLOWER-123' \ --user '[email protected]:<api_token>' \ --header 'Accept: application/json'

Example: The Request Type of a Service Space Work Item

Look for an entry like this in the response:

{ "customfield_10023": { "requestType": { "name": "Technical Task" } } }

This shows that customfield_10023 is the request type field in this instance.

Since the name of a request type can change, it is often safer to use its ID:

${pi.fields.customfield_10023.requestType.name == 'Technical support'} ${pi.fields.customfield_10023.requestType.id == '48'}

The same approach works for other complex fields. These paths go inside ${ }:

  • Priority: pi.fields.priority.name
  • Status: pi.fields.status.name
  • User fields: pi.fields.assignee.displayName
  • Any other custom field: pi.fields.customfield_xxxxxx

Example: JSON response of a work item in a service space

{ "expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations,customfield_10023.requestTypePractice", "id": "28406", "self": "https://your-jira-instance.atlassian.net/rest/api/latest/issue/28406", "key": "FLOWER-123", "fields": { "statuscategorychangedate": "2025-02-23T18:42:28.260+0100", "lastViewed": "2025-02-24T16:17:03.503+0100", "customfield_10100": [], "priority": { "self": "https://your-jira-instance.atlassian.net/rest/api/2/priority/5", "iconUrl": "https://your-jira-instance.atlassian.net/images/icons/priorities/trivial.svg", "name": "Trivial", "id": "5" }, "assignee": "user object", "status": { "self": "https://your-jira-instance.atlassian.net/rest/api/2/status/10000", "description": "This was auto-generated by JIRA Service Desk during workflow import", "iconUrl": "https://your-jira-instance.atlassian.net/images/icons/status_generic.gif", "name": "Waiting for support", "id": "10000", "statusCategory": { "self": "https://your-jira-instance.atlassian.net/rest/api/2/statuscategory/1", "id": 1, "key": "undefined", "colorName": "medium-gray", "name": "No Category" } }, "creator": "user object", "reporter": "user object", "customfield_11011": { "id": "13", "name": "Time to first response", "_links": { "self": "https://your-jira-instance.atlassian.net/rest/servicedeskapi/request/28406/sla/13" }, "completedCycles": [], "ongoingCycle": { "startTime": { "iso8601": "2025-02-23T19:42:27+0200", "jira": "2025-02-23T18:42:27.531+0100", "friendly": "Yesterday 7:42 PM", "epochMillis": 1740332547531 }, "breachTime": { "iso8601": "2025-02-24T18:00:00+0200", "jira": "2025-02-24T17:00:00.000+0100", "friendly": "Today 6:00 PM", "epochMillis": 1740412800000 }, "breached": false, "paused": false, "withinCalendarHours": true, "goalDuration": { "millis": 28800000, "friendly": "8h" }, "elapsedTime": { "millis": 27632453, "friendly": "7h 40m" }, "remainingTime": { "millis": 1167547, "friendly": "19m" } } }, "aggregateprogress": { "progress": 0, "total": 0 }, "progress": { "progress": 0, "total": 0 }, "votes": { "self": "https://your-jira-instance.atlassian.net/rest/api/2/issue/PC-3/votes", "votes": 0, "hasVoted": false }, "worklog": { "startAt": 0, "maxResults": 20, "total": 0, "worklogs": [] }, "issuetype": { "self": "https://your-jira-instance.atlassian.net/rest/api/2/issuetype/10126", "id": "10126", "description": "For customer support issues. Created by Jira Service Desk.", "iconUrl": "https://your-jira-instance.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/11228?size=medium", "name": "Support", "subtask": false, "avatarId": 11228, "hierarchyLevel": 0 }, "project": { "self": "https://your-jira-instance.atlassian.net/rest/api/2/project/10915", "id": "10915", "key": "PC", "name": "ProcessCreators", "projectTypeKey": "service_desk", "simplified": false }, "created": "2025-02-23T18:42:27.531+0100", "customfield_10023": { "_links": { "jiraRest": "https://your-jira-instance.atlassian.net/rest/api/2/issue/28406", "web": "https://your-jira-instance.atlassian.net/servicedesk/customer/portal/8/PC-3", "agent": "https://your-jira-instance.atlassian.net/browse/PC-3", "self": "https://your-jira-instance.atlassian.net/rest/servicedeskapi/request/28406" }, "requestType": { "_expands": [ "field" ], "id": "48", "_links": { "self": "https://your-jira-instance.atlassian.net/rest/servicedeskapi/servicedesk/8/requesttype/48?expand=requestTypePractice" }, "name": "Technical support", "description": "Need help installing, configuring, or troubleshooting? Select this to request assistance.", "helpText": "", "issueTypeId": "10126", "serviceDeskId": "8", "portalId": "8", "groupIds": [ "12" ], "icon": { "id": "11196", "_links": { "iconUrls": { "48x48": "https://your-jira-instance.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/11196?size=large", "24x24": "https://your-jira-instance.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/11196?size=small", "16x16": "https://your-jira-instance.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/11196?size=xsmall", "32x32": "https://your-jira-instance.atlassian.net/rest/api/2/universal_avatar/view/type/SD_REQTYPE/avatar/11196?size=medium" } } } }, "currentStatus": { "status": "Waiting for support", "statusCategory": "UNDEFINED", "statusDate": { "iso8601": "2025-02-23T19:42:27+0200", "jira": "2025-02-23T18:42:27.531+0100", "friendly": "Yesterday 7:42 PM", "epochMillis": 1740332547531 } } }, "customfield_10024": { "id": "12", "name": "Time to resolution", "_links": { "self": "https://your-jira-instance.atlassian.net/rest/servicedeskapi/request/28406/sla/12" }, "completedCycles": [], "ongoingCycle": { "startTime": { "iso8601": "2025-02-23T19:42:27+0200", "jira": "2025-02-23T18:42:27.531+0100", "friendly": "Yesterday 7:42 PM", "epochMillis": 1740332547531 }, "breachTime": { "iso8601": "2025-02-25T18:00:00+0200", "jira": "2025-02-25T17:00:00.000+0100", "friendly": "25/Feb/25 6:00 PM", "epochMillis": 1740499200000 }, "breached": false, "paused": false, "withinCalendarHours": true, "goalDuration": { "millis": 57600000, "friendly": "16h" }, "elapsedTime": { "millis": 27632545, "friendly": "7h 40m" }, "remainingTime": { "millis": 29967455, "friendly": "8h 19m" } } }, "updated": "2025-02-23T18:42:33.013+0100", "description": "Your issue description", "summary": "Your issue summary" } }

User Objects

User fields such as assignee, creator, and reporter are objects:

Example: JSON of a user object

{ "self": "https://your-jira-instance.atlassian.net/rest/api/2/user?accountId=557058%3Afc60faa5-f40d-4344-ae2b-4becd42a7914", "accountId": "557058:fp60faa5-xxxx-4344-yyyy-4becd42z7914", "avatarUrls": { "48x48": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/557058:fp60faa5-xxxx-4344-yyyy-4becd42z7914/a3075f1a-94e7-4ea6-91bd-a0165342fcfd/48", "24x24": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/557058:fp60faa5-xxxx-4344-yyyy-4becd42z7914/a3075f1a-94e7-4ea6-91bd-a0165342fcfd/24", "16x16": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/557058:fp60faa5-xxxx-4344-yyyy-4becd42z7914/a3075f1a-94e7-4ea6-91bd-a0165342fcfd/16", "32x32": "https://avatar-management--avatars.us-west-2.prod.public.atl-paas.net/557058:fp60faa5-xxxx-4344-yyyy-4becd42z7914/a3075f1a-94e7-4ea6-91bd-a0165342fcfd/32" }, "displayName": "Power User", "active": true, "timeZone": "Europe/Athens", "accountType": "atlassian" }
ℹ️

The JSON structure in Jira Cloud and Jira Data Center is almost identical but may differ in some details.
Always check the data structure in your own Jira instance.

External Data

Sometimes a decision depends on external data that is not stored in Jira. Flower lets you store such data as JSON on the process instance and use it in expressions.

Store External Data with the Jira REST API

Jira provides a REST endpoint for issue properties  that stores JSON on a work item. Store your data in the property flowerVariables of the process instance:

curl --request PUT \ --url 'https://your-domain.atlassian.net/rest/api/3/issue/FLOWER-2/properties/flowerVariables' \ --user '[email protected]:<api_token>' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --data '{"some":"value"}'

The JSON can be as complex as you need, but it must not exceed 32,768 bytes. You can then use it in an expression on a transition:

${pi.properties.flowerVariables.some == 'value'}

With the data above, this expression is true.

Last updated on