Getting Started

JIRA Extension Points

ScriptRunner for JIRA Cloud

ScriptRunner for JIRA Cloud allows you to extend the functionality of JIRA Cloud, executing scripts to interact with JIRA as Post Functions or Event Listeners. Scripts can take actions such as updating an issue during a transition or performing a calculation and storing the result in a custom field on the issue. Administrators have the power of the Groovy programming language at their disposal to respond to events and transitions, manipulating JIRA using the REST API.

Differences to ScriptRunner for JIRA Server

ScriptRunner for JIRA Cloud follows the same principles as the original ScriptRunner but due to the differences in extension points between the Atlassian Connect framework used to write add-ons in the cloud and the Plugins V2 framework used in behind the firewall implementations, the execution model is significantly different. Scripts in ScriptRunner Cloud do not execute within the same process as JIRA and so must interact with JIRA using the REST APIs rather than the JAVA APIs. Atlassian Connect is also inherently asynchronous which means that when a script executes the user may see the page load before the script has completed.

At this time it is not possible to implement Scripted Fields, Workflow Conditions and Validators or JQL Functions. Scheduled jobs such as Escalation Service are on the road map and we are investigating what is be possible as an alternative to Scripted Fields.


Asynchronous execution is at the heart of the Atlassian Connect Framework that ScriptRunner must use to extend JIRA Cloud. JIRA fires webhooks when events and transitions occur and any user interface elements are loaded in iframes. It is not possible to veto, cancel or otherwise prevent JIRA performing an action using ScriptRunner. Scripts themselves are written synchronously, that is REST API calls are made in a blocking style (although async methods are provided too). This page has an excellent introduction to the Atlassian Connect framework for those that are interested.

Isolated Execution

All user provided code is executed in an isolated container. ScriptRunner for JIRA Cloud is a service that is shared by many customers so isolation is very important that when executing scripts. Isolation is a goal that has been core to the design of ScriptRunner for JIRA from its inception. Each customer has a container that is theirs and theirs only. It is not possible for the code of one customer to have been run in the container of another. Containers are also designed to be single use, however it is possible that containers will be re-used for execution on occasion, but script writers should not rely on this. Disk storage in these containers is ephemeral, any changes written to the disk will be discarded.


All interaction with JIRA must be through the provided REST APIs. Atlassian provides comprehensive documentation including JSON Schema for responses and request bodies which describe the shape of the JSON. For effective usage of ScriptRunner it is important to have an understanding of how to interact with JIRA using the REST API.

Authentication and Authorisation

All REST requests made from ScriptRunner are performed as the ScriptRunner user. It is not possible to perform requests on behalf of another user at the current time, without putting user credentials in a script. Atlassian Connect Add-ons must also register for API scopes. These work in a similar way to the permissions that are granted to iOS or Android apps, when installing the JIRA Administrator can see the scopes that any given add-on is requesting. ScriptRunner requests all scopes as it is not know beforehand what any given user will want to do. Even given this there are restrictions on the APIs that are available to ScriptRunner.

  • No private APIs are available, only those specifically white-listed by Atlassian

  • Scopes are documented for

  • It is possible for Project Administrators to remove access to a particular add-on by modifying the permissions for a project.

Authentication from user scripts is handled by an authentication proxy builtin to ScriptRunner. Each script invocation is given a temporary authentication token to use to make requests into this proxy which will then perform the necessary request signing to make the authenticated request to your JIRA instance. In this way authenticated requests happen transparently. Tokens that are handed to scripts are only valid for two minutes. Responses that come through the proxy have URLs modified to go through the proxy so that URLs in the JSON response can be used directly without manipulation. For example the following code shows how to fetch a project from an issue get.

String projectUrl = get('/rest/api/2/issue/TP-25').asObject(Map).body['fields']['project']['self']



The HTTP library provided by ScriptRunner is Unirest. Unirest is a simple, lightweight library that makes interacting with REST APIs simple and straightforward. It was chosen due to the minimal dependencies (based on Apache HTTP Client 4.3), flexibility (JSON mapping support is built in and object mapping provided by Jackson) and the clarity of API.

Unirest, Unirest.get,, Unirest.put, Unirest.head and Unirest.options are included in all scripts as import static which means no imports are needed in order to make HTTP requests. The base url to the ScriptRunner authentication proxy is filled in along with the authentication headers so making REST calls is as simple as copying and pasting from the JIRA REST documentation


Get issue types:


Create an issue:

def projectKey = 'TP'
def taskType = get('/rest/api/2/issuetype').asObject(List).body.find { it['name'] == 'Task' }['id'] (1)
post('/rest/api/2/issue') (2)
    .header('Content-Type', 'application/json')
                fields: [
                        summary    : 'Task Summary',
                        description: "Don't forget to do this!.",
                        project    : [
                                key: projectKey
                        issuetype  : [
                                id: taskType
    .asString().body (3)
1 Use the issuetype REST endpoint to get all issuetypes, parse into a list, and find the type with name Task, then take the id of that type
2 Make the post request to create the issue in the specified project with the specified issuetype
3 It is important to note that the request is only made when one of the as* methods are called. asString() takes response and makes it available in .body as a String. asObject(Object) will use Jackson to parse the response from JSON

Update an issue:

put('/rest/api/2/issue/TP-25') (1)
    .header('Content-Type', 'application/json')
            fields: [
                    summary    : 'Updated Task Summary',
    .asString().status (2)
1 Method to update an issue is a put see documentation
2 The REST request responds with a 204 (no content) so there is no point reading the body

Static Type Checking

Static type checking is a feature intended to provide information to you about whether your script is correctly written.

The first thing to understand about Groovy is that it’s a dynamic language. Amongst many other facets of dynamic languages, this means that method and property names are looked up when your code is run, not when it’s compiled, unlike java.

Take a very simple but complete script:

That is, call the method bar() on the object foo. Unlike java, this script will compile without errors, but when you run it you will get a MissingPropertyException, as foo hasn’t been defined. This behaviour is useful as there are many circumstances that could make this code execute successfully, for instance an object called foo, or a closure getFoo(), being passed to the script’s binding.

Although Groovy is a dynamic language, we can compile scripts in a manner that checks method and property references at compile time. The static type checking (STC) feature does just that, so that you can see any problems in your scripts when you are writing them, as opposed to when they execute.

It’s important to understand that when your scripts are executed, they are always compiled dynamically. When they are compiled for STC, the resulting generated bytecode is thrown away.

There are limitations to the type checker - it’s quite possible to write code that is displayed as having errors, but which is perfectly valid and will execute fine. Some examples of this are:

  • using certain Builders

  • using closures where the parameter types can’t be inferred

  • nested maps where type information is not available

At the risk of repetition, the type checker is not always correct. It is a best-efforts attempt to let you know if there are problems with your code - but please let us know if you find something that should compile but doesn’t. Note also that your code could still have runtime errors that won’t be found until it executes.

If we were writing a condition where we wanted to check the number of issues returned from a search is zero, we might inadvertently write:

boolean empty = = 0

STC is telling us that we are trying to set the estimate, rather than retrieve it. We meant to use the equality operator:

boolean empty = == 0

Code areas will show a green dot, which tells us that the code is syntactically correct, and the methods and properties have been checked.

STC in the cloud will provide additional help for bindings, ensuring that properties access matches the JSON Schema provided by Atlassian