Workflow Restriction and Validator Expression Examples

You can create custom workflow restrictions and workflow validators using Jira expressions. This page provides a library of example expressions you can use and adapt to suit your workflow. The examples are grouped by topic to help you find what you need.

What are Jira expressions?

Jira expressions is a scripting language developed by Atlassian for evaluating logic in Jira Cloud. They use a JavaScript-like syntax and give you access to a set of context variables, such as issue, user, and project, that represent the current state of Jira at the point the expression is evaluated.

Jira Expressions are not JavaScript. Only the properties and methods documented in the Jira Expressions type reference are available. Expressions must always return a Boolean (true or false). A result of true allows the transition to proceed; false blocks it.

Limitations using Jira expressions

Some restrictions apply to evaluating expressions using the Jira expression frameworkAn expression can execute up to 10 expensive operations. Expensive operations are those that load additional data, such as entity properties, comments, or custom fields. 

Atlassian provides information on Jira expression types and lists the properties and functions which trigger expensive operations so that users can determine the operation complexity of expressions being evaluated. 

To check the complexity of an expression before deploying it, you can use the Evaluate Jira expression REST API operation with the meta.complexity expand parameter to see the number of expensive operations triggered.

Workflow restrictions vs validators: Which to use?


RestrictionsValidators
When it runsBefore the transition screen is shownAfter the user submits the transition screen
The effect when falseThe transition is hiddenThe transition is visible but blocked with an error message
Custom error messageNoYes
Can access transition-time dataNo (screen not shown yet)Yes (e.g. comment or attachment being added right now)

Most expressions on this page work for both. They check the existing state of the work item, such as fields, links, sub-tasks, dates, and user permissions, which is available in both contexts. A small subset are validator-only and labelled as such. 

Choosing between them:

  • Use a restriction to silently hide a transition until conditions are met (for example, hide Close until all sub-tasks are done).
  • Use a validator when you want the transition visible but need to guide the user with a clear error message (for example, "Please add a comment before closing").

Examples

Users and permissions

These expressions control who can perform a transition based on the current user's identity, space role, or group membership. Use them to ensure only the right people can move work items through specific workflow stages.

Restrict to a space role

Checks whether the current user belongs to the specified space role:

javascript
user.getProjectRoles(project).some(p => p.name == "Developers")

Replace Developers with the space role name. Roles are space-scoped; user groups are global. See Atlassian's documentation for managing project roles.

Restrict to a user group

Checks whether the current user belongs to the specified group:

javascript
user.groups.includes('jira-administrators')

Replace jira-administrators with the group name. Roles are space-scoped; user groups are global. See Atlassian's documentation to view, create, or delete a group.

Check if the current user is in a named list of users

Checks whether the current user is in a specified list, by display name:

javascript
['User A', 'User B', 'User C'].includes(user.displayName)

Check if the current user is within a group from a specified list

Checks whether the current user belongs to at least one group from a specified list:

javascript
['Group A', 'Group B', 'Group C'].some(g => user.groups.includes(g))

Specify that the current user must be in a defined list of users

Checks whether the current user is in a specified list, by account ID:

You can also use user.displayName, however, account IDs are unique and stable; they never change even if a user updates their display name.

javascript
['accountIdHere', 'accountIdHere'].includes(user.accountId)

Require specific users in a space to create work items of a specified type

Checks whether the current user is permitted to create the specified work item type. In this example, only members of the Developers role can create Bug work items; all other users can create any work item type:

javascript
issue.issueType.name == 'Bug' ? user.getProjectRoles(project).map(p => p.name).includes("Developers") : true

The current logged-in user has added at least one comment

Checks that the current logged-in user has added at least one comment to the work item:

javascript
issue.comments.some(c => c.author.accountId == user.accountId)

User selected in a custom user picker field

Checks whether a specific user is selected in a user picker custom field. Single user picker:

javascript
// by display name issue.customfield_10213?.displayName == 'A User' // or using accountId issue.customfield_10213?.accountId == '5cf7c174eba28b4ea84a7cb5'

Multi-user picker:

javascript
issue.customfield_10219 ? issue.customfield_10219.some(user => user.displayName == 'A User') : false

Group selected in a custom group picker field

Checks whether a specific group is selected in a group picker custom field. Single user picker:

javascript
issue.customfield_10077?.name == 'jira-admins'

Multi-group picker:

javascript
issue.customfield_10078 ? issue.customfield_10078.some(c => c.name == 'jira-admins') : false

Fields and values

These expressions validate the content of work item fields before or during a transition. Use them to enforce that required fields are populated, that field values meet specific criteria, or that text matches an expected pattern.


How to find a custom field ID

See Atlassian's documentation for information on how to find a custom field ID.

Field is required

Checks that a text, single-select, multi-select, radio button, checkbox, or cascading select field is not empty:

javascript
issue.customfield_10040 != null

Cascading select, parent value only:

javascript
issue.customfield_10266?.value != null

Cascading select, both parent and child:

javascript
// You cannot set the child without the parent issue.customfield_10266?.child?.value != null

Rich text custom field, required and non-empty:

javascript
let plainTextValue = value => typeof value == 'Map' ? new RichText(value).plainText : value; plainTextValue(issue.customfield_10080 != null) && plainTextValue(issue.customfield_10080) != ''

At least one of two fields must have a value

Checks that at least one of two fields has a value:

Replace customfield_12345 and customfield_67890 with the IDs of the fields in your space. You can extend this pattern with additional || or && clauses for more fields.

javascript
issue.customfield_12345 != null || issue.customfield_67890 != null

Multi-select or checkbox must contain a specific value

Checks that a multi-select or checkbox field contains a specific value:

javascript
issue.customfield_10263 ? issue.customfield_10263.some(option => option.value == "End Users") : false

Multi-select or checkbox must equal an exact set of values

Checks that a multi-select or checkbox field contains exactly a specified set of values:

javascript
issue.customfield_10214 ? issue.customfield_10214.map(option => option.value) == ['Yes', 'No'] : false

Multi-select or checkbox must contain one specific value

Checks that a multi-select or checkbox field contains at least one specific value:

javascript
issue.customfield_10214 ? issue.customfield_10214.some(option => option.value == "A") : false

Work item description must contain more than X characters

Checks that the work item description is longer than a minimum number of characters:

Change 30 to the minimum number of characters required.

javascript
issue.description.plainText.length > 30

Field changed at any point in the work item's lifetime

Checks whether a specified field has been changed at any point in the work item's lifetime:

Replace Field Name with the name of the field to check.

javascript
issue.changelogs.some(c => c.items.some(i => i.field == 'Field Name'))

Field changed in the most recent update

Checks whether a specified field was changed in the most recent update:

javascript
// changelogs[0] is the most recent entry issue.changelogs[0].items.some(i => i.field == 'Field Name')

Regular expression match on description or a custom text field

These examples check that the description or custom text field value matches a regular expression.

The Regular Expression condition is only available on strings, including text fields and option values.

Regular expression matches description:

javascript
issue.description.plainText.match("SRJ-\\d+") != null

Regular expression matches custom text field value:

javascript
issue.customfield_10206 ? issue.customfield_10206.match("SRJ-\\d+") != null : false

Require at least one fix version

Checks that the work item has at least one fix version:

javascript
issue.fixVersions.length > 0

Require at least one component

Checks that the work item has at least one component:

javascript
issue.components.length > 0

Comments

These expressions validate the comments on a work item. Use them to require a comment during a transition, enforce a minimum comment length, or, in Jira Service Management, control whether comments are public or internal.

Require a comment during transition (validator only)

Checks that a comment is being added during the current transition:

A comment being added during a transition has not yet been persisted, so its id is null. Because the transition screen hasn't been shown yet when a restriction runs, this expression only works as a Validator rule.

javascript
issue.comments.some(comment => comment.id == null)

All comments must meet a minimum length

Checks that all comments on the work item are longer than a minimum number of characters:

Change 10 to the minimum number of characters required.

javascript
issue.comments.every(comment => comment.body.plainText.length > 10)

All existing comments must be public (Jira Service Management)

Checks that all existing comments on the work item are public:

javascript
issue.comments.every(c => !c.properties["sd.public.comment"].internal)

Comment added during transition must be internal (Jira Service Management) (validator only)

Checks that any comment being added during the current transition is marked as internal:

The internal property is a string ('true'/'false') for comments being added during a transition (i.e. id == null), not a boolean. Because this checks for a comment being added right now, it only works as a Validator rule.

javascript
issue.comments.every(c => c.id != null || c.properties["sd.public.comment"].internal == 'true')

Attachments

These expressions validate the attachments on a work item. Use them to ensure that required files are present before a transition can proceed, or to enforce that a minimum number of attachments are added during the transition itself.

Require at least one PDF attachment

Checks that the work item has at least one PDF attachment:

javascript
issue.attachments.some(attachment => attachment.mimeType == 'application/pdf')

Require a minimum number of PDF attachments

Checks that the work item has more than a specified number of PDF attachments:

Change 2 to the minimum number of PDFs required.

javascript
issue.attachments.filter(attachment => attachment.mimeType == 'application/pdf').length > 2

Require a minimum number of PDFs added during the current transition (validator only)

Checks that a specified number of PDF attachments are being added during the current transition:

Change > 1 to the minimum number of PDFs required. The transition screen must include the Attachments field so users can submit attachments during the transition. Because this checks for attachments being added right now, it only works as a Validator rule.

javascript
issue.attachments.filter( attachment => attachment.id == null && attachment.mimeType == 'application/pdf' ).length > 1

Require a minimum number of attachments added during the current transition (validator only)

Checks that a specified number of attachments are being added during the current transition:

Change 3 to the minimum number of attachments required. The transition screen must include the Attachments field so users can submit attachments during the transition. Because this checks for attachments being added right now, it only works as a Validator rule.

javascript
issue.attachments.filter(attachment => attachment.id == null).length == 3

Dates and times

These expressions compare date and time values on a work item against the current date. Use them to enforce time-based rules, such as requiring a work item to be resolved within a certain period or ensuring a due date is set within an acceptable range.

The following system fields return a Date object: created, updated, resolutionDate.

Work item was created more than 7 days ago

Checks that the work item was created more than 7 days ago:

javascript
issue.created < new Date().minusDays(7)

Work item was updated within the last 2 hours

Checks that the work item was updated within the last 2 hours:

javascript
issue.updated > new Date().minusHours(2)

Work item was resolved within the last 30 days

Checks that the work item was resolved within the last 30 days:

resolutionDate custom fields can be null. Always check for null before comparing.

javascript
issue.resolutionDate ? issue.resolutionDate >= new Date().minusDays(30) : false

Work item is due within the next 3 months

Checks that the work item is due within the next 3 months:

dueDate custom fields can be null. Always check for null before comparing. The dueDate field returns a CalendarDate object (date only, no time).

javascript
issue.dueDate ? issue.dueDate <= new CalendarDate().plusMonths(3) : false

Date custom field is 30 or more days in the future

Checks that a date custom field is 30 or more days in the future:

Date custom fields store a string value. Convert it to a CalendarDate using new CalendarDate(issue?.customfield_10200).

javascript
issue.customfield_10200 ? new CalendarDate(issue.customfield_10200) >= new CalendarDate().plusDays(30) : false

Date-time custom field is less than 6 hours in the future

Checks that a date-time custom field value is less than 6 hours in the future:

DateTime custom fields store a string value. Convert it to a Date using new Date(issue?.customfield_10217).

javascript
issue.customfield_10217 ? new Date(issue.customfield_10217) <= new Date().plusHours(6) : false

Sub-tasks

These expressions validate the state of a work item's sub-tasks. Use them to enforce that sub-tasks exist, are assigned, or have reached a required status before the parent work item can transition.

Require sub-tasks

Checks that the work item has at least one sub-task:

Change > 0 to the minimum number of sub-tasks required.

javascript
issue.subtasks.length > 0

Sub-tasks must be done

Checks that all sub-tasks are in the Done status:

Change Done to the required status name.

javascript
issue.subtasks.every(subtask => subtask.status.name == 'Done')

Sub-tasks must be in progress

Checks that all sub-tasks are In Progress:

This expression cannot be used on the Create transition. Use it on the Start Progress transition.

javascript
issue.subtasks.every(subtask => subtask.status.name == 'In Progress')

Sub-tasks must have an assignee

Checks that all sub-tasks have an assignee:

javascript
issue.subtasks.every(subtask => subtask.assignee != null)

Sub-tasks of a specific type must be at a specific status

Checks that all sub-tasks of a specific type are at a specific status:

javascript
issue.subtasks .filter(s => s.issueType.name == 'Scope Change') .every(d => d.status.name == 'Done')

Linked work items

These expressions validate the linked work items associated with a work item. Use them to enforce that links exist, or that linked work items have reached a required status or resolution before the current work item can transition.

Require at least one linked work item

Checks that the work item has at least one linked work item:

You can edit this expression to require other fields. For example, replace links with fixVersions or components.

javascript
issue.links.length > 0

All linked work items must be in a specific status

Checks that all linked work items are in a specific status:

javascript
issue.links.every(l => l.linkedIssue.status.name == 'Done')

All linked work items of a specific link type must be in a specific status

Checks that all linked work items of a specific link type are in a specific status:

direction can be 'inward' or 'outward', corresponding to the inward/outward labels of the link type (e.g. "is blocked by" / "blocks").

javascript
issue.links .filter(l => l.direction == 'inward') .filter(l => l.type.inward == 'is blocked by') .every(l => l.linkedIssue.status.name == 'Done')

All linked work items must have a resolution

Checks that all linked work items have a resolution:

javascript
issue.links.every(l => l.linkedIssue.resolution != null)

All linked work items of a specific link type must have a resolution

Checks that all linked work items of a specific link type have a resolution:

javascript
issue.links .filter(l => l.type.inward == 'is blocked by') .every(l => l.linkedIssue.resolution != null)

Work item type and sprint

These expressions check the work type or sprint state of a work item. Use them to restrict transitions based on what kind of work item it is, or to ensure it is part of an active sprint before work can begin.

Work item must be in the current active sprint

Checks that the work item is in the current active sprint:

javascript
issue.sprint?.state == 'active'

All stories in an epic must be done

Checks that all stories in the epic are in the Done status:

javascript
issue.isEpic && issue.stories.every(story => story.status.name == 'Done')

Verify work type

Checks whether the work type is Bug or Task:

javascript
["Bug", "Task"].includes(issue.issueType.name)

Status and history

This expression inspects the changelog of a work item. Use it to check whether a work item has previously passed through a specific status.

Work item has been in a specific status previously

Checks that the work item has previously been in a specific status:

javascript
issue.changelogs.some(c => c.items.some(i => i.toString == 'In Progress') )

Common mistakes

These are the most frequent errors when writing Jira expressions for restrictions and validators. Each example shows what goes wrong and how to fix it.

Nested if statements

Jira expressions do not support nested if statements. For example, the following expression will fail:

javascript
if (issue.summary.length > 10 ) { if(issue.assignee) { // ... } }

You can instead combine this into a single expression, as shown in the example below:

javascript
if (issue.summary.length > 10 && issue.assignee) { // ... }

Returning a list instead of a boolean

Restrictions and validators must return a boolean. The following expression returns a list of objects and will fail:

javascript
// ❌ Will NOT work — returns a list, not a boolean issue.comments.map(c => c.body) // Use .some() or .every() to produce a boolean instead // ✅ Correct issue.comments.every(comment => comment.body.plainText.length > 10)

Missing issue. prefix on a field reference

A common typo when checking multiple fields; the second field reference is missing the issue. prefix:

javascript
// ❌ Will NOT work — customfield_67890 is undefined issue.customfield_12345 != null || customfield_67890 != null // ✅ Correct issue.customfield_12345 != null || issue.customfield_67890 != null
On this page