Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents


Children Display

titleGDPR - necessary permissions

Permissions required: "Browse users and groups" -> global permission.

According GDPR, this app does not store any personal data by itself. All user entered data are stored within Jira properties within your own Jira instance hosted by Atlassian: no data will be stored outside your Jira instance at all!

Create/Modify Group Sign-Off fields

Please create new fields of type "Group Sign-Off" as many as you need, like "Steering Committee", "Product Board" etc.

  1. Switch into your Jira Administration → System → Group Sign-Off fields (you have to scroll down the menu to find this item) or alternatively, click on "configure" within the related section on page "Manage apps" as shown on the sample screenshot on the right.
  2. Click on the icon "+" to add a new field
    1. enter a unique name for your new field
    2. on tab "(dynamic) Rule", enter your rule for determine the related deciders (users) as well as the sign-off formula
    3. on tab "Context", enter all valid issue types as well as projects to specify which issues will display this new field
    4. on tab "Context", you can optionally upload own icons for the buttons "decline" and "sign-off" being presented to the deciders
    5. on tab "Auto-Transitions", you can optionally specify the transition ID of your related workflow (see: allowed issues as configured on tab "Context"), which will be triggered automatically in case of a final "sign-off" or "decline"
    6. finish by hitting on the button "Create"
  3. Click on the pencil-icon in the middle of the screen to edit that row's field configurations (analog to create such a field)
  4. Click on the tray-icon in the middle of the screen to move that row's field on tab "inactive fields": all data are kept but inactive fields will not be displayed on screen. On tab "inactive fields", you can finally delete a field or revert your action by moving it back to "active fields".

Static Rule

You have to determine, who is allowed to make a decision. Using the feature of setting a default value of a customfield, you can define it once and it will be taken over while creating a new issue automatically.

A definition of a decision is divided into two parts and stored as value within the related customfield:

  • the list of responsible users being able and allowed to decide and
  • a rule to determine the final result of the decision.

The list starts at the first character, first line with the login name of the responsible user: one user per line. Each line has to be terminated by a line-break. If the login name is unknown, it will not be displayed on the screen. The list of users end with a blank line for a better overview.

The rule for the decision starts with "sign-off=" followed by a boolean expression. Internal validation of the rule will be done by replacing all login names by their related decisions (sign-off = true and decline = false, pending = nothing). As soon as this rule results in a final TRUE or FALSE, it triggers the configured listener (see below). Having no individual decisions at the beginning or necessary pending ones, the result cannot be determined and nothing happens. The boolean expression can contain brackets "(" and ")" as well as "AND" and "OR" to describe the relationship of individual decisions. If the IT-boss or her/his representative can decide, one of both sign-offs is enough, and the business product owner has to sign-off as well the rule would be like: (boss OR representative) AND productOwner.

GDPR makes it necessary to use account ids

If an automatic conversion from a user name or email into an account id is not possible, which depends on various scenarios and cannot be simply answered, you have to use the account id of deciders instead. Generally, that should not be necessary but in case you need it for uniqueness, you can find the accountId of a user as described below.

How to get the account id of a user?

The easiest way to find your account id is to click on your icon on the sidebar (left/most down icon with your avatar image) and then on the "Profile" link. Then, have a look at the URL within your browser: your accountId is the string after the last "/" marked bold in the sample below:

Sample: https://******

Such accountId has to be adjusted by replacing the colon (":") by double underscores ("__") and all minus ("-") by a single underscore ("_").

titleSimple Example of a definition

sign-off=(fpolscheit OR representative)

 If you want to put additional information to the user name, you can add this as a comment straight after the user name and enclosed by /* and */:

titleSimple Example with additional user infos
fpolscheit /* Manager */
representative /* Alternative Voting */

sign-off=(fpolscheit /* Manager */ OR representative /* Alternative Voting */)

By clicking on "Update", each user name will be replaced by it's related account id before storage within a Jira property due to GDPR. If the automatic migration to an account id is not possible, a WARNING hint will be written at the end of the rule as comment and a related warning sign will be displayed on the right side of the field name within the overview list of all group sign-off fields. Such scenario occurs, if for example a specified user name is not unique (API request /rest/api/3/user/search?query=username results in multiple account ids).

Use comments to display them as additional infos:

Dynamic Rule(s) for more complex but flexible approaches

Instead of explicitly declare a list of users and a sign-off rule using boolean algebra, you can specify a dynamic rule, which extracts the users out of referred other fields like multi-user pickers. A dynamic rule MUST start with a first line just containing "// conditional rule". Then, users and a rule has to be defined as described below:

Within a dynamic rule, the reserved words in blue within the following complex example are mandatory!
Within a condition, you can use all methods provided by JIRA's issue API. The helper object provides the following methods:

  • contains(collection, string)
  • contains(collection, id as number)
  • getCF(issue, customfieldName)
  • getCFms(issue, customfieldName) to retrieve a customfield's value in milliseconds
  • getUsersByCustomfield(issue, customfieldName, operand) to retrieve a (list of) user(s)
    customfieldName can be any custom field of type single-user picker or multi-user picker
  • *) getUsersByProjectRole(issue, projectRoleName, operand) to retrieve a list of users
  • *) getUsersByGroup(issue, groupName, operand) to retrieve a list of users
  • concat(list, operand) to append a list of users using logical operand like "AND" or "OR" within rule definition and "," within user definition
  • log(data) to write data into the user's browser console output.

If you are using getUsersByCustomfield() etc. then the name of the referred element (e.g. field) will be displayed in gray in addition to the user name. So, a user knows her/his context if displayed multiple times, for example by belonging to various internal roles.

(warning) *) According to Atlassian's restrictions, access to users per project role or group can only be done by users with admin permissions! Therefore, each time you modify the members of a group or project role, you have to click on the "sync" button as admin in order to provide this infos to the Group Sign-Off app in the context of all logged-in users.

titleComplex Example with different syntax: use a customfield (multi-user or user picker) to dynamically specify voters

// conditional rule

users =""+helper.getUsersByCustomfield(issue,"Board Members",",");
rule =""+helper.getUsersByCustomfield(issue,"Board Members","OR");

Please use the exact syntax above for dynamically specifying the list of user(s) based on the referred custom field. Users must be a comma separated list, that why the operand is a comma. The rule must be a sequence of users, concatinated by a boolean operand (AND or OR). By the first voting, the content of the customfield, referenced by name as parameter, will be taken over and the members will become a fix list.

Instead of using the custom field's name, you can also use it's ID: please put that number into the quotes like "10023" instead of "Board Members".

titleComplex Example with different syntax: a set of conditional definitions depending on issue's data
// conditional rule
if ( helper.contains(issue.components, "Component A") ) {
     users = "fpolscheit, admin";
     rule    = "fpolscheit OR admin";
} else if ( helper.contains(issue.components, "Component B") && ( == "Major")) { 
     users = "admin, tester"; 
     rule    = "admin AND tester"; 
} else { 
     users = "admin"; 
     rule    = "admin"; 

GDPR makes it necessary to use account ids

If an automatic conversion from a user name or email into an account id is not possible, which depends on various scenarios and cannot be simply answered, you have to use the account id of deciders instead. Generally, that should not be necessary but in case you need it for uniqueness, you can find the accountId of a user as described below.

How to get the account id of a user?

The easiest way to find your account id is to click on your icon on the sidebar (left/most down icon with your avatar image) and then on the "Profile" link. Then, have a look at the URL within your browser: your accountId is the string after the last "/" marked bold in the sample below:

Sample: https://******

Such accountId has to be adjusted by replacing the colon (":") by double underscores ("__") and all minus ("-") by a single underscore ("_") if being used as a hardcoded reference within your dynamic rule.

Access issue's data for conditions

You can also access all fields of an issue, like within the following sample of extracting the issue's reporter, and use fields' information for approval:

titleComplex Example accessing issue's functions to dynamically retrieve approver name(s), here: issue's reporter

// conditional rule
users = "" + issue.reporter.accountId.replace(/:/g,"__").replace(/-/g,"_"); // according to GDPR: use accountID instead of user name
rule    = "" + issue.reporter.accountId.replace(/:/g,"__").replace(/-/g,"_");

You can use "helper.log(issue)" to display all available fields and the elements' structure within your browser's console.

If you are using multiple custom fields to retrieve all users, who have to decide all together, you have to concatenate the custom fields content and use a logical operator within the dynamic rule. The sample below illustrates proper usage.


titleConcat of multiple custom fields' contents

// conditional rule

users =""+helper.getUsersByCustomfield(issue,"Board Members",",") + helper.concat(helper.getUsersByCustomfield(issue,"Manager",",") , ",");
rule =""+helper.getUsersByCustomfield(issue,"Board Members","OR") + helper.concat(helper.getUsersByCustomfield(issue,"Manager","OR") , "AND");

Another sample of displaying the Group Sign-Off field in a certain status, only:

titleRestricted displaying depending on issue's status

// conditional rule

users ="";
rule ="";
if ( == "Pending Approval") {
    users += helper.getUsersByProjectRole(issue,"Approver",",");
    rule += helper.getUsersByProjectRole(issue,"Approver","AND");

Another sample of displaying the Group Sign-Off field depending on a certain value of a multi-select custom field:

titleRestricted displaying depending on an issue's multi-select custom field

// conditional rule

users ="";
rule ="";
if (issue.customfield_xyz.find(item => item.value== "my option")) {
    users += helper.getUsersByProjectRole(issue,"Approver",",");
    rule += helper.getUsersByProjectRole(issue,"Approver","AND");

Behave differently if the issue's reporter is a member of a specified project role:
Code Block
titleIf reporter belongs to project role 'X' then use special behavior
// conditional rule
if (helper.removeContexts(helper.getUsersByProjectRole(issue,"Administrators",",")).indexOf(issue.reporter.accountId.replace(/:/g,"__").replace(/-/g,"_")) > -1) {
    // issue has been created by a member of project role 'Administrators'
    // no decisions are necessary: ignore group sign-Off (do nothing)
    users = "";
    rule = "";
} else {
    users =""+helper.getUsersByCustomfield(issue,"Approvers",",");
    rule =""+helper.getUsersByCustomfield(issue,"Approvers","OR");

Special Function(s)

  • check(param1, param2, ...)

with param-1: Boolean value TRUE or FALSE, whereas true indicates sign-off and false indicates decline
param-2: threshold, resp. minimum amount of decisions as specified within param1 to return the value of param1, otherwise this function will return null
param-3, ..., param-n: comma-separated list of deciders

titleAdvanced Sample: sign-Off rule based on function calls and boolean algebra


 sign-off=check(true, 2, fpolscheit,representative,anotherPerson) OR check(false, 1, fpolscheit,representative,anotherPerson)

If at least one decider declines, the sign-off will be "declined" or if at least two deciders have voted for sign-off, the result will be "signed-off".

Based on 3 possible results of function check(null, true, false), the sequence of both function calls combined with OR is important: if you change both, it will not work properly! Based on boolean logic, false OR null results in null, whereas null OR false results in false which is what you would like to get. So, the check for declined votes via check(false,...) has to be the second part after the OR-connector.

A more complex sample combines the check-function with other dynamic helper-functions retrieving sets of deciders: at least 2 deciders out of the project roles "Developers" and "Administrators" have to sign-off, so that this rule switches into the state "signed-off". But if 1 decider declines, it results into "declined", immediately.

titleComplex Sample: combination of check-function with dyn. helper-functions

// conditional rule
users=""+helper.getUsersByProjectRole(issue,"Developers",",")+helper.concat(helper.getUsersByProjectRole(issue,"Administrators",",") , ",")
rule="check(true, 2, " + helper.getUsersByProjectRole(issue,"Developers",",") + helper.concat(helper.getUsersByProjectRole(issue,"Administrators",",") , ",") + ") OR check(false, 1, " + helper.getUsersByProjectRole(issue,"Developers",",") + helper.concat(helper.getUsersByProjectRole(issue,"Administrators",",") , ",") + ")"

Expanation: using a dynamic rule, that rule must get a string in quotes like rule = "check(true, 2, x, y, z) OR check(false, 1, x, y, z)", whereas x, y, z is build by helper-functions like a concat of getUsersByProjectRole().

Modify your workflow(s) using new Condition(s)

At the point of time when a group decision shall happen, it does not make sense to click on the related button/transition and continue the workflow without synchronization with the result of the decision. So, you have to block further processing by adding a workflow condition by "Block transition until signed-off" or "Block transition until declined"

Having configured such a condition, the related workflow transition will not be visible and clickable for any users as it is blocked by the condition. As soon as a final decision is evaluated, the configured workflow transition will be executed in the context of the logged in user based on the result (signed-off / declined).

Additional options

Additional options can be added at the end of the definition of any rule like shown below.

Disable delegation of decisions

  • optionDelegation=false|true(is default) : prevent from delegation by deciders

Disable displaying individual users

  • optionDisplayNoUsers : by default, users being identified as deciders are displayed including their voting, visible for all users. Using this option these details are omitted to protect privacy of individual decisions to third-parties not being deciders of that decision by themselves.

How to configure "Revert (all | my) decisions"

Display decision buttons for deciders only if their decision has got an effect on the final result

  • optionDecisionWithEffectOnly by default, all deciders will see the buttons for declining or signing off. If you want to reduce that just for deciders, whose decision has got an immediate effect on the final result then use this option.
    In that case, a decline as well as a sign-off decision for all deciders without any already taken decisions will be simulated. This depends heavily on the complexity of your dynamic rule! So, it might occur that you have to contact me via email at to assist or adapt in specific cases.

Email Notifications for all deciders

As admin, please ensure that all your deciders are members of the default Jira group named "users": it's not enough to be a member of "jira-users", only! Best practice: assign all your users to the group "users".

Then, you can dis-/enable email notifications within the app's configuration main panel by clicking on the related checkbox as displayed on the sample screen copy on the right side.

This notification feature uses Jira's internal email notification service. The sender cannot be configured by any third-party app but consider the following Atlassian documentation to set the sender email per project as a system admin: .

Alternatively, each decider can create a Jira filter based on JQL, saved that under a suitable name and activate filter subscription for this named filter, which is a standard feature of Jira: you can specify the frequency and at what day and time you will receive an email with a summary of all outstanding issues to be decided etc. That is very helpful, especially if you are spammed with too many emails. Also see: Usage (Jira Cloud)#Notificationsfordeciders(searchissuesforpendingdecisionsviaJQL)