Jira REST API examples

This guide contains different examples of how to use the Jira REST API, including how to query issues, create an issue, edit an issue, and others.

The reference documentation for the Jira Server platform REST API is here:  Jira Server platform REST API. If you've never used the Jira REST APIs before, we recommend that you also read the overview About the Jira REST APIs.

The examples on this page use curl. If an input file is required, it is denoted by the --data @filename syntax and the file data is shown separately.

Creating an issue examples

Creating an issue using the Jira REST API is as simple as making a POST with a JSON document. To create an issue, you will need to know certain key metadata, like the ID of the project that the issue will be created in, or the ID of the issue type. You can request the create metadata for all issue types across all projects by using the createmeta resource.

For example:

1
http://localhost:8080/rest/api/2/issue/createmeta

If you only want a subset of this information, specify the desired projects and issue types as query parameters. For example, this request will return the create metadata for the Bug issue type in the Jira project:

1
http://localhost:8080/rest/api/2/issue/createmeta?projectKeys=JRA&issuetypeNames=Bug&expand=projects.issuetypes.fields

For more detailed examples of requesting metadata, see the examples in the sections later.

Creating an issue using a project key and field names

This is a basic example of how to create an issue using the Jira REST API.

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X POST \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/
Input data
1
2
3
4
5
6
7
8
9
10
11
12
13
{
    "fields": {
       "project":
       {
          "key": "TEST"
       },
       "summary": "REST ye merry gentlemen.",
       "description": "Creating of an issue using project keys and issue type names using the REST API",
       "issuetype": {
          "name": "Bug"
       }
   }
}
Response
1
2
3
4
5
{
   "id":"39000",
   "key":"TEST-101",
    "self":"http://localhost:8080/rest/api/2/issue/39000"
}

Creating an issue using a project ID and issue type ID

This example uses the project ID and issue type ID rather than the key and name respectively. This is useful if you only have the IDs. For example, your integration or script may have previously requested and saved only the IDs of the project and issue type.

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X POST \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/
Input data
1
2
3
4
5
6
7
8
9
10
11
12
13
{
    "fields": {
       "project":
       {
          "id": "10110"
       },
       "summary": "No REST for the Wicked.",
       "description": "Creating of an issue using IDs for projects and issue types using the REST API",
       "issuetype": {
          "id": "1"
       }
   }
}
Response

The response provides the issue ID, key, and the URL to the issue. You can use this to GET additional data, PUT updates, and so on via the REST API.

1
2
3
4
5
{
   "id":"39001",
   "key":"TEST-102",
   "self":"http://localhost:8080/rest/api/2/issue/39001"
}

Creating a sub-task

A sub-task is essentially a special type of issue, so the sub-task creation request and response are very similar to issue creation. Creating a sub-task has two important differences:

  • The issueType field must correspond to a sub-task issue type (you can use /issue/createmeta to discover sub-task issue types).
  • You must provide a parent field in the issue create request containing the ID or key of the parent issue.
Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X POST \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/
Input data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
    "fields":
    {
        "project":
        {
            "key": "TEST"
        },
        "parent":
        {
            "key": "TEST-101"
        },
        "summary": "Sub-task of TEST-101",
        "description": "Don't forget to do this too.",
        "issuetype":
        {
            "id": "5"
        }
    }
}
Response

The response provides the sub-task ID, key, and the URL to the issue (which can then be used to GET additional data, PUT updates, and so on) via the REST API.

1
2
3
4
5
{
   "id":"39002",
   "key":"TEST-103",
   "self":"http://localhost:8080/rest/api/2/issue/39002"
}

Creating an issue using custom fields

In the Jira REST API, custom fields are uniquely identified by the field ID, as the display names are not unique within a Jira instance. For example, you could have two fields named "Escalation date", one with an ID of "12221" and one with an ID of "12222".

A custom field is actually referenced by customfield\_ + the field ID, rather than just the field ID.

For example, the "Story points" custom field with ID = "10000" is referenced as customfield\_10000 for REST calls. You can get this reference identifier by requesting the create metadata for the issue type.

The example below uses a custom free text field named "Explanation" that has an ID of 11050. Note that we reference the field by customfield\_11050 and that the name of the field "Explanation" is not used anywhere.

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X POST \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/
Input data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
    "fields": {
       "project":
       {
          "key": "TEST"
       },
       "summary": "Always do right. This will gratify some people and astonish the REST.",
       "description": "Creating an issue while setting custom field values",
       "issuetype": {
          "name": "Bug"
       },       
       "customfield_11050" : "Value that we're putting into a Free Text Field."
   }
}
Response

Again, the issue created is returned in the response.

1
2
3
4
5
{
   "id":"39002",
   "key":"TEST-103",
    "self":"http://localhost:8080/rest/api/2/issue/TEST-103"
}

Setting custom field data for other field types

The examples below show how to set the values for different types of custom fields in the input data. Note that you should retrieve custom field ID from createmeta endpoint.

CascadingSelectField
1
"customfield_10001": {"value": "green", "child": {"value":"blue"} }

The value associated with "name" ("green" in this example) is the parent option selected, then "blue" is the child option).

DatePickerField
1
"customfield_10002": "2011-10-03"

The format is: YYYY-MM-DD

DateTimeField
1
"customfield_10003": "2011-10-19T10:29:29.908+1100"

This format is ISO 8601: YYYY-MM-DDThh:mm:ss.sTZD

FreeTextField
1
"customfield_10004": "Free text goes here.  Type away!"
GroupPicker
1
"customfield_10005": { "name": "jira-developers" }

Like users, groups are specified by name or ID.

MultiGroupPicker
1
"customfield_10007": [{ "name": "admins" }, { "name": "jira-developers" }, { "name": "jira-users" }]

Like users, groups are specified by name or ID.

MultiSelect
1
"customfield_10008": [ {"value": "red" }, {"value": "blue" }, {"value": "green" }]
MultiUserPicker
1
"customfield_10009": [ {"name": "charlie" }, {"name": "bjones" }, {"name": "tdurden" }]  

Array of users.

NumberField
1
"customfield_10010": 42.07

Just a number (not a number in a string).

ProjectPicker
1
"customfield_10011": { "key": "JRADEV" }

You can also specify the project by project ID.

1
{ "id":"10000" }
RadioButtons
1
"customfield_10012": { "value": "red" }

You can also specify the selection by ID.

SelectList
1
"customfield_10013": { "value": "red" }

You can also specify the selection by ID.

SingleVersionPicker
1
"customfield_10014": { "name": "5.0" }

You can also specify the version by ID.

TextField
1
"customfield_10015": "Is anything better than text?"
URLField
1
"customfield_10016": "http://www.atlassian.com"
UserPicker
1
"customfield_10017": { "name":"brollins" }
VersionPicker
1
"customfield_10018": [{ "name": "1.0" }, { "name": "1.1.1" }, { "name": "2.0" }]  

You can also specify a version by ID.

Adding a worklog entry during create

If you want to set the time tracking fields in a Jira issue when creating the issue, the create data should include a section like the following:

1
2
3
4
"timetracking": {
   "originalEstimate": "1d 2h",
   "remainingEstimate": "3h 25m"
}

Time tracking must be enabled to set these fields. In addition, if you use the Jira "Legacy" time tracking mode (set by a Jira Administrator), then only the remaining estimate can be set, so the originalestimate field should not be included in the REST request.

Request

The same as the other examples, the create is a POST:

1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X POST \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/
Input data

Again, this data is if Legacy mode for time tracking is off.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
   "fields": {
       "project":
       {
          "key": "TEST"
       },
       "summary": "Who seeks a faultless friend RESTs friendless.",
       "description": "Creating an issue and setting time tracking fields",
       "issuetype": {
          "name": "Bug"
        },       
        "timetracking":
        {
           "originalEstimate": "1d 2h",
           "remainingEstimate": "3h 25m"
        }
    }
}
Response
1
2
3
4
5
{
   "id":"39003",
   "key":"TEST-104",
    "self":"http://localhost:8080/rest/api/2/issue/TEST-104"
}

Editing an issue examples

The examples in this section show you how to edit an existing issue using the Jira REST API. There are two types of examples in this section:

  1. Editing an issue by updating the value of a field. Examples:
    • Assigning an issue to a user.
    • Updating multiple fields in one request.
  2. Editing an issue by using the SET, ADD, and REMOVE operations. Not all fields support all operations, but as a general rule, single value fields support SET, whereas multi-value fields support SET, ADD, and REMOVE, where SET replaces the field contents while ADD and REMOVE add or remove one or more values from the the current list of values. Examples: Adding a component. Setting the components field. Adding a component and removing another component in the same request. Updating multiple fields.

To edit an issue, you need to know certain key metadata, like the editable fields and the operations that they support.

You can request this data for an issue by using the editmeta resource. For example:

1
http://localhost:8080/rest/api/2/issue/JRA-13/editmeta

Note that the editmeta resource does not work with PUT operations. You should only use it to get data.

Assigning an issue to a user

This example assigns an issue to a user with the username "charlie".

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X PUT \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/QA-31
Input data
1
2
3
4
5
{
   "fields": {
       "assignee":{"name":"charlie"}
   }
}
Response

Status code of "204 No Content".

Updating multiple fields in one request

This example updates the summary, description, and two custom fields.

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X PUT \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/QA-31
Input data
1
2
3
4
5
6
7
8
{
    "fields" : {
        "summary": "Summary",
        "description": "Description",
        "customfield_10200" : "Test 1",
        "customfield_10201" : "Value 1"
    }
}
Response

Status code of "204 No Content".

Adding a component

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X PUT \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/QA-31

example input data

1
2
3
4
5
{
   "update" : {
       "components" : [{"add" : {"name" : "Engine"}}]
   }
}
Response

Status code of "204 No Content".

Setting the components field

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X PUT \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/QA-31

This is an example input data:

1
2
3
4
5
{
    "update" : {
        "components" : [{"set" : [{"name" : "Engine"}, {"name" : "Trans/A"}]}]
    }
}
Response

Status code of "204 No Content".

Adding a component and removing another component in the same request

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X PUT \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/QA-31

This is an example input data:

1
2
3
4
5
{
    "update" : {
        "components" : [{"remove" : {"name" : "Trans/A"}}, {"add" : {"name" : "Trans/M"}}]
    }
}

Note: The "Engine" component (if it exists) remains unaffected by this update.

Response

Status code of "204 No Content".

Updating multiple fields

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u charlie:charlie \
   -X PUT \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/issue/QA-31

This is an example input data:

1
2
3
4
5
6
7
{
    "update" : {
        "components" : [{"remove" : {"name" : "Trans/A"}}, {"add" : {"name" : "Trans/M"}}],
        "assignee" : [{"set" : {"name" : "harry"}}],
        "summary" : [{"set" : "Big block Chevy"}]
    }
}
Response

Status code of "204 No Content".


Adding a comment examples

The examples in this section show you how to add a comment to an existing issue using the Jira REST API. There are two types of examples in this section:

  1. Adding a comment using the comment resource. This resource simply adds a comment and nothing else. It is also possible to add a comment as a side-effect of another operation like "edit issue" or "transition issue". This resource is particularly useful if the logged in user does not have "edit" permission for the issue, but does have the "add comment" permission. Examples: Adding a comment. Adding a comment and setting the security level in the same request.
  2. Adding a comment when editing an issue, that is, using the edit issue method. Examples:

    • Adding a comment using the edit issue method.
    • Adding a comment and updating the issue description using the edit issue method.

Adding a comment

This example request adds a comment to an existing issue.

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u fred:fred \
   -X POST \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8081/rest/api/2/issue/QA-31/comment

This is an example input data:

1
2
3
{
    "body": "This is a comment regarding the quality of the response."
}
Response

You should just receive a response with a status of "201" with the full JSON representation of the added comment.

Adding a comment and setting the security level in the same request

This example request adds a comment and sets the security level to an existing issue.

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u fred:fred \
   -X POST \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8081/rest/api/2/issue/QA-31/comment
Input data
1
2
3
4
5
6
7
{
    "body": "This is a comment that only administrators can see.",
    "visibility": {
        "type": "role",
        "value": "Administrators"
    }
}

Adding a comment using the edit issue method

This example request adds a comment to an existing issue via the edit issue method. Note that only one comment at a time can be added when updating an issue via this resource.

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u fred:fred \
   -X PUT \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8081/rest/api/2/issue/QA-31
Input data
1
2
3
4
5
6
7
8
9
10
11
{
   "update": {
      "comment": [
         {
            "add": {
               "body": "It is time to finish this task"
            }
         }
      ]
   }
}

Adding a comment and updating the issue description using the edit issue method

This example request updates the description of an existing issue and adds a comment explaining why via the edit issue method.

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u fred:fred \
   -X PUT \
   --data {see below} \
   -H "Content-Type: application/json" \
   http://localhost:8081/rest/api/2/issue/QA-31
Input data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
   "update": {
   "description": [
         {
            "set": "JIRA should also come with a free pony"
         }
      ],
      "comment": [
         {
            "add": {
               "body": "This request was originally written in French, which most of our developers can't read"
            }
         }
      ]
   }
}

Searching for issues examples

The examples in this section show you how to search for issues using JQL via the Jira REST API.

Examples in this section:

  • Searching for issues assigned to a particular user.
  • Searching for issues assigned to particular user and restricting the number of results.
  • Searching for issues assigned to particular user with ordered results.
  • Searching for issues and restricting the issue fields returned in the results.
  • Searching for issues using POST.

Searching for issues assigned to a particular user

This example request searches for issues assigned to a user with the username "charlie". A single URL parameter (jql) that contains the JQL query is provided in the request.

Request
1
2
3
4
5
6
curl \
   -D- \
   -u charlie:charlie \
   -X GET \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/search?jql=assignee=charlie
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
    "expand": "schema,names",
    "startAt": 0,
    "maxResults": 50,
    "total": 6,
    "issues": [
        {
            "expand": "html",
            "id": "10230",
            "self": "http://localhost:8080/rest/api/2/issue/BULK-62",
            "key": "BULK-62",
            "fields": {
                "summary": "testing",
                "timetracking": null,
                "issuetype": {
                    "self": "http://localhost:8080/rest/api/2/issuetype/5",
                    "id": "5",
                    "description": "The sub-task of the issue",
                    "iconUrl": "http://localhost:8080/images/icons/issue_subtask.gif",
                    "name": "Sub-task",
                    "subtask": true
                },
.
.
.
                },
                "customfield_10071": null
            },
            "transitions": "http://localhost:8080/rest/api/2/issue/BULK-62/transitions",
        },
        {
            "expand": "html",
            "id": "10004",
            "self": "http://localhost:8080/rest/api/2/issue/BULK-47",
            "key": "BULK-47",
            "fields": {
                "summary": "Cheese v1 2.0 issue",
                "timetracking": null,
                "issuetype": {
                    "self": "http://localhost:8080/rest/api/2/issuetype/3",
                    "id": "3",
                    "description": "A task that needs to be done.",
                    "iconUrl": "http://localhost:8080/images/icons/task.gif",
                    "name": "Task",
                    "subtask": false
                },
.
.
.
                  "transitions": "http://localhost:8080/rest/api/2/issue/BULK-47/transitions",
        }
    ]
}

Searching for issues assigned to particular user and restricting the number of results

This example request searches for issues assigned to a user with the username "charlie" and restricts the number of results to a specified number of issues.

Two additional URL parameters are provided in the request: startAt and maxResults. These parameters specify the starting issue returned in the JQL results and the number of issues from that starting issue respectively.

Request
1
2
3
4
5
6
curl \
   -D- \
   -u charlie:charlie \
   -X GET \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/search?jql=assignee=charlie&startAt=2&maxResults=2
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
    "expand": "schema,names",
    "startAt": 2,
    "maxResults": 2,
    "total": 6,
    "issues": [
        {
            "expand": "html",
            "id": "10123",
            "self": "http://localhost:8080/rest/api/2/issue/BULK-38",
            "key": "BULK-38",
            "fields": {
                "summary": "aaaaa",
                "timetracking": null,
                "issuetype": {
                    "self": "http://localhost:8080/rest/api/2/issuetype/5",
                    "id": "5",
                    "description": "The sub-task of the issue",
                    "iconUrl": "http://localhost:8080/images/icons/issue_subtask.gif",
                    "name": "Sub-task",
                    "subtask": true
                },
.
.
.
            },
            "transitions": "http://localhost:8080/rest/api/2/issue/BULK-38/transitions",
        },
        {

            "expand": "html",
            "id": "10108",
            "self": "http://localhost:8080/rest/api/2/issue/BULK-32",
            "key": "BULK-32",
            "fields": {
                 "summary": "subtasks are important, too",
                 "timetracking": null,
                 "issuetype": {
                     "self": "http://localhost:8080/rest/api/2/issuetype/5",
                     "id": "5",
                     "description": "The sub-task of the issue",
                     "iconUrl": "http://localhost:8080/images/icons/issue_subtask.gif",
                     "name": "Sub-task",
                     "subtask": true
                 },
.
.
.
            },
            "transitions": "http://localhost:8080/rest/api/2/issue/BULK-32/transitions",
        }
    ]
}

Searching for issues assigned to particular user with ordered results

This example request searches for issues assigned to a user with the username "charlie" and orders the returned issues by due date. The ordering is specified by using an order by clause in the JQL query itself (not via a URL parameter in your REST API call).

Request
1
2
3
4
5
6
curl \
   -D- \
   -u charlie:charlie \
   -X GET \
   -H "Content-Type: application/json" \
   http://localhost:8080/rest/api/2/search?jql=assignee=charlie+order+by+duedate
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
    "expand": "schema,names",
    "startAt": 0,
    "maxResults": 50,
    "total": 6,
    "issues": [
        {
            "expand": "html",
            "id": "10123",
            "self": "http://localhost:8080/rest/api/2/issue/BULK-38",
            "key": "BULK-38",
            "fields": {
                "summary": "aaaaa",
                "timetracking": null,
                "issuetype": {
                    "self": "http://localhost:8080/rest/api/2/issuetype/5",
                    "id": "5",
                    "description": "The sub-task of the issue",
                    "iconUrl": "http://localhost:8080/images/icons/issue_subtask.gif",
                    "name": "Sub-task",
                    "subtask": true
                },
.
.
.
            },
            "transitions": "http://localhost:8080/rest/api/2/issue/BULK-38/transitions",
        },
        {

            "expand": "html",
            "id": "10108",
            "self": "http://localhost:8080/rest/api/2/issue/BULK-32",
            "key": "BULK-32",
            "fields": {
                 "summary": "subtasks are important, too",
                 "timetracking": null,
                 "issuetype": {
                     "self": "http://localhost:8080/rest/api/2/issuetype/5",
                     "id": "5",
                     "description": "The sub-task of the issue",
                     "iconUrl": "http://localhost:8080/images/icons/issue_subtask.gif",
                     "name": "Sub-task",
                     "subtask": true
                 },
.
.
.
            },
            "transitions": "http://localhost:8080/rest/api/2/issue/BULK-32/transitions",
        }
    ]
}

Searching for issues and restricting the issue fields returned in the results

This example request searches for issues assigned to a user with the username "charlie" and restricts the issue fields returned to a specified set. The fields restriction is specified by supplying an additional URL parameter  fields, which lists the Jira fields returned in the JQL results. Each Jira field in the list should be comma-separated, for example, fields=id,key.

Keep in mind that some extra data is always returned in the JSON response.

Request
1
2
3
4
5
6
curl \
   -D- \
   -u charlie:charlie \
   -X GET \
   -H "Content-Type: application/json" \
   'http://localhost:8080/rest/api/2/search?jql=project=QA+order+by+duedate&fields=id,key'
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
    "expand": "schema,names",
    "startAt": 0,
    "maxResults": 50,
    "total": 18,
    "issues": [
        {
            "expand": "html",
            "id": "10050",
            "self": "http://localhost:8080/rest/api/2/issue/QA-19",
            "key": "QA-19",
            "transitions": "http://localhost:8080/rest/api/2/issue/QA-19/transitions"        
        },
        {
            "expand": "html",
            "id": "10051",
            "self": "http://localhost:8080/rest/api/2/issue/QA-20",
            "key": "QA-20",
            "transitions": "http://localhost:8080/rest/api/2/issue/QA-20/transitions"        
        },
.
.
.
        {
            "expand": "html",
            "id": "10053",
            "self": "http://localhost:8080/rest/api/2/issue/QA-22",
            "key": "QA-22",
            "transitions": "http://localhost:8080/rest/api/2/issue/QA-22/transitions"        
        },
        {
            "expand": "html",
            "id": "10389",
            "self": "http://localhost:8080/rest/api/2/issue/QA-35",
            "key": "QA-35",
            "transitions": "http://localhost:8080/rest/api/2/issue/QA-35/transitions"        
        }
    ]
}

Searching for issues using POST

If your JQL query is too large to specify in a URL parameter, you can POST your JQL query (in JSON format) to the Jira REST API search resource instead. Any additional URL parameters (apart from the url parameter) described above must be included in your JSON-formatted JQL query.

Request
1
2
3
4
5
6
7
curl \
   -D- \
   -u admin:admin \
   -X POST \
   -H "Content-Type: application/json" \
   --data '{"jql":"project = QA","startAt":0,"maxResults":2,"fields":["id","key"]}' \
   "http://localhost:8080/rest/api/2/search"
Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    "maxResults": 2,
    "startAt": 0,
    "total": 18,
    "expand": "schema,names",
    "issues": [
        {
            "expand": "html",
            "id": "10393",
            "key": "QA-36",
            "self": "http://localhost:8080/rest/api/2/issue/QA-36",
            "transitions": "http://localhost:8080/rest/api/2/issue/QA-36/transitions"
        },
        {
            "expand": "html",
            "id": "10389",
            "key": "QA-35",
            "self": "http://localhost:8080/rest/api/2/issue/QA-35",
            "transitions": "http://localhost:8080/rest/api/2/issue/QA-35/transitions"
        }
    ]
}

Getting metadata for creating issues examples

The Jira REST API allows you to discover the fields and data available and required for creating issues. For this we use the createmeta resource.

Examples in this section:

  • Discovering project and issue type data.
  • Discovering issue field data.

Discovering project and issue type data

To create an issue in Jira, you first need to specify a project and issue type. These together are referred to in Jira as an Issue context and are used to find the Jira schemes that control what fields are available for an issue, what the default values are, and what fields are mandatory.

Using the createmeta resource you can discover the project and issue types.

Request
1
2
3
4
5
6
curl \
   -D- \
   -u fred:fred \
   -X GET \
   -H "Content-Type: application/json" \
   http://localhost:8081/rest/api/2/issue/createmeta
Response

The response consists of an array of projects and each project contains an array of issue types that apply to that project.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
{
    "expand": "projects",
    "projects": [
        {
            "self": "http://localhost:8081/rest/api/2/project/XSS",
            "id": "10020",
            "key": "XSS",
            "name": "<iframe src=\"http://www.google.com\"></iframe>",
            "avatarUrls": {
                "16x16": "http://localhost:8081/secure/projectavatar?size=small&pid=10020&avatarId=10011",
                "48x48": "http://localhost:8081/secure/projectavatar?pid=10020&avatarId=10011"
            },
            "issuetypes": [
                {
                    "self": "http://localhost:8081/rest/api/2/issuetype/1",
                    "id": 1,
                    "name": "Bug",
                    "iconUrl": "http://localhost:8081/images/icons/bug.gif"
                },
                {
                    "self": "http://localhost:8081/rest/api/2/issuetype/2",
                    "id": 2,
                    "name": "New Feature",
                    "iconUrl": "http://localhost:8081/images/icons/newfeature.gif"
                },
.
.
.                {
                    "self": "http://localhost:8081/rest/api/2/issuetype/5",
                    "id": 5,
                    "name": "Sub-task",
                    "iconUrl": "http://localhost:8081/images/icons/issue_subtask.gif"
                }
            ]
        },
        {
            "self": "http://localhost:8081/rest/api/2/project/BULK",
            "id": "10000",
            "key": "BULK",
            "name": "Bulk Move 1",
            "avatarUrls": {
                "16x16": "http://localhost:8081/secure/projectavatar?size=small&pid=10000&avatarId=10020",
                "48x48": "http://localhost:8081/secure/projectavatar?pid=10000&avatarId=10020"
            },
            "issuetypes": [
                {
                    "self": "http://localhost:8081/rest/api/2/issuetype/1",
                    "id": 1,
                    "name": "Bug",
                    "iconUrl": "http://localhost:8081/images/icons/bug.gif"
                },
.
.
.
                {
                    "self": "http://localhost:8081/rest/api/2/issuetype/5",
                    "id": 5,
                    "name": "Sub-task",
                    "iconUrl": "http://localhost:8081/images/icons/issue_subtask.gif"
                }
            ]
        },
        {
            "self": "http://localhost:8081/rest/api/2/project/BLUK",
            "id": "10001",
            "key": "BLUK",
            "name": "Bulk Move 2",
            "avatarUrls": {
.
.
.
.
        }
    ]
}

If you know the projects or issue types you are interested in, you can restrict the list using the projectKeys, projectIds, issuetypeNames, and issuetypeIds query parameters. For example:

1
2
3
4
5
6
curl \
   -D- \
   -u fred:fred \
   -X GET \
   -H "Content-Type: application/json" \
   http://localhost:8081/rest/api/2/issue/createmeta?projectKeys=QA,XSS

Discovering issue field data

When you have a project and issue type, you can retrieve the information for the issue fields by supplying the expand query parameter with the projects.issuetypes.fields value.

Request
1
2
3
4
5
6
curl \
   -D- \
   -u fred:fred \
   -X GET \
   -H "Content-Type: application/json" \
   http://localhost:8081/rest/api/2/issue/createmeta?projectKeys=QA&issuetypeNames=Bug&expand=projects.issuetypes.fields
Response

The response consists of an array of projects and each project contains an array of issue types that apply to that project.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
{

    "expand": "projects",
    "projects": [
        {
            "expand": "issuetypes",
            "self": "http://localhost:8081/rest/api/2/project/QA",
            "id": "10010",
            "key": "QA",
            "name": "QA",
            "avatarUrls": {
                "16x16": "http://localhost:8081/secure/projectavatar?size=small&pid=10010&avatarId=10011",
                "48x48": "http://localhost:8081/secure/projectavatar?pid=10010&avatarId=10011"
            },
            "issuetypes": [
                {
                    "expand": "fields",
                    "self": "http://localhost:8081/rest/api/2/issuetype/1",
                    "id": 1,
                    "name": "Bug",
                    "iconUrl": "http://localhost:8081/images/icons/bug.gif",
                    "fields": {
                        "summary": {
                            "required": true,
                            "schema": {
                                "type": "string",
                                "system": "summary"
                            },
                            "operations": [
                                "set"
                            ]
                        },
                        "timetracking": {
                            "required": false,
                            "operations": [ ]
                        },
                        "issuetype": {
                            "required": true,
                            "schema": {
                                "type": "issuetype",
                                "system": "issuetype"
                            },
                            "operations": [ ],
                            "allowedValues": [
                                {
                                    "id": "1",
                                    "name": "Bug",
                                    "description": "A problem which impairs or prevents the functions of the product.",
                                    "iconUrl": "http://localhost:8081/images/icons/bug.gif"
                                }
                            ]
                        },
                        "customfield_10080": {
                            "required": false,
                            "schema": {
                                "type": "array",
                                "items": "string",
                                "custom": "com.atlassian.jira.plugin.system.customfieldtypes:labels",
                                "customId": 10080
                            },
                            "operations": [ ]
                        },
.
.
.
.
                        "customfield_10010": {
                            "required": false,
                            "schema": {
                                "type": "array",
                                "items": "string",
                                "custom": "com.atlassian.jira.plugin.system.customfieldtypes:labels",
                                "customId": 10010
                            },
                            "operations": [ ]
                        },
                        "customfield_10071": {
                            "required": false,
                            "schema": {
                                "type": "array",
                                "items": "string",
                                "custom": "com.atlassian.jira.plugin.system.customfieldtypes:textfield",
                                "customId": 10071
                            },
                            "operations": [ ]
                        }
                    }
                }
            ]
        }
    ]

}

If you prefer, by omitting the projectKeys and issuetypeNames parameters you can retrieve all the issue field data at once for all projects and issue types. However, this approach could amount to a very large response and could take some time to build on systems with a large number of projects and issue types.

Other examples

The examples in this section show you more advanced use cases for the REST API, like calling the REST API from a script or an app.

Examples in this section:

  • Calling the REST API from a script: Graphing image links.
  • Calling the REST API from an app: Quickview inline dialog app.

This example shows you how to write a small python script that will use REST interface to graph the relationships between issues in Jira site.

To simplify the REST requests, you can use the small helper library called restkit. This library is not strictly necessary – after all, REST is just an HTTP. However, restkit can be used for convenience. You also rely on pygraphviz to do the actual graphing for you too. To use it you will need Graphviz installed.

Using the Jira REST API is straightforward:

  1. You make an HTTP call.
  2. Get some data back.
  3. Then do something with that data.

In the following example, 95% of the code is doing something other than interacting with the Jira REST API. So before you see the full example, let's highlight the actual REST usage out of context to show how simple it usually is. This example uses Python:

1
2
3
4
5
6
7
8
9
resource = Resource(url + '/rest/api/2/issue/%s' % key, filters=[auth])
response = resource.get(headers = {'Content-Type' : 'application/json'})
    if response.status_int == 200:
        # Not all resources will return 200 on success. There are other success status codes. Like 204. We've read
        # the documentation though and know what to expect here.
        issue = json.loads(response.body_string())
        return issue
    else:
        return None

This performs a GET on the issue, checks for a successful response, and then parses the JSON response into a Python dictionary. The filters=\[auth\] line is how you tell restkit to perform BASIC Authentication. Later on, you'll reach into this Python dictionary to grab the data you want for your work.

1
2
3
4
fields = issue['fields']
if fields.has_key('subtasks'):
    for subtask in issue['fields']['subtasks']:
        # do work with a subtask

You also get an Epic Link custom field ID with following:

1
2
3
4
5
6
7
8
9
def get_epic_id(url, key, auth):
    resource = Resource(url + ('/rest/api/latest/issue/%s?expand=names' % key), filters=[auth])
    response = resource.get(headers={'Content-Type': 'application/json'})
    if response.status_int == 200:
        for field_id, field_name in json.loads(response.body_string())['names'].items():
            if field_name == 'Epic Link':
                return field_id
    else:
        return None

You can view the full source on Bitbucket.

You can see the script's command line options using the standard command:

1
python draw-chart.py --help

You can test this against your Jira site with:

1
python draw-chart.py --user=username --password=password --jira=<url-of-your-jira-site>

The output should look similar to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Fetching JRADEV-1391
Fetching JRADEV-2062
Fetching JRADEV-2063
Fetching JRADEV-1107
Fetching JRADEV-112
Fetching JRADEV-1108
Fetching JRADEV-1218
Fetching JRADEV-1219
Fetching JRADEV-1220
Fetching JRADEV-1221
Fetching JRADEV-1684
Fetching JRADEV-2064
Fetching JRADEV-1390
Fetching JRADEV-1389
Fetching JRADEV-1388
Fetching JRADEV-2125
Fetching JRADEV-1264
Fetching JRADEV-1256
Writing to issue_graph.png

Open the issue\_graph.png to show an image that should look something like this:

Blue lines with arrows denote Sub-tasks.

Calling the REST API from an app: Quickview inline dialog app

This example shows you how to create a Jira app that uses the REST API. We want to look through all the comments on the issue and add a little tooltip that will pop-up when you hover over a link to a Jira issue.

The pop-up should contain a "quick view" of information about the target issue (similar to the example shown in the following image) so that you do not have to click the issue's link to see this information.

You can achieve this by using a Web Resource Context. This lets your app put JavaScript just on the View Issue page of Jira.

  1. Define the Web Resource Context in the atlassian-plugin.xml file:

    1
    2
    3
    4
    5
    <web-resource key="remote-link" name="Remote Issue Linking">
        <dependency>com.atlassian.auiplugin:ajs</dependency>
        <resource name="java-demo-plugin.js" type="download" location="js/java-demo-plugin.js"/>
        <context>jira.view.issue</context>
    </web-resource>
  2. Have java-demo-plugin.js look in the comment body for URLs that "look like" they might point to Jira issues.

  3. Obtain the JSON representation of the issue using Jira's REST API, do some quick formatting on it, and put it into an AUI InlineDialog.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
define('issue-hover', ['ajs'], function(AJS){
    AJS.toInit(function() {
        var i = new Date().getTime();
        AJS.$("#issue_actions_container").find('.action-body a').each(function() {
            if (this.href.match(/\/browse\/[A-Z]+\-\d+$/)) {
                var split = this.href.split('/browse/');
                var base = split[0];
                var key = split[1];
                var options = { cacheContent: true, onHover: true, showDelay: 400, hideDelay: 400, closeOthers: false, width: 500 }
                var draw = function(contents, trigger, showPopup) {
                    AJS.$.getJSON(base + '/rest/api/latest/issue/' + key, function(data) {
                        var fields = data["fields"];
                        contents.empty();
                        contents.append(
                            "<ul class=\"item-details\">"
                            + "<li>"
                            + "<dl><dt>Summary: </dt>" + "<dd>" + fields["summary"] + "</dd></dl>"
                            + "<dl><dt>Type: </dt>" + "<dd>" + fields["issuetype"]["name"] + "</dd></dl>"
                            + "<dl><dt>Priority: </dt>" + "<dd>" + fields["priority"]["name"] + "</dd></dl>"
                            + "<dl><dt>Status: </dt>" + "<dd>" + fields["status"]["name"] + "</dd></dl>"
                            + "<dl><dt>Assignee: </dt>" + "<dd>" + fields["assignee"]["name"] + "</dd></dl>"
                            + "<dl><dt>Description: </dt>" + "<dd>" + fields["description"] + "</dd></dl>"
                            + "</li></ul>");
                        contents.append("<form id=\"add-watch\" name=\"watch\" action=\"\">");
                        AJS.$("<input type=\"button\" name=\"button\" value=\"Watch\"/>").click(function() {
                            // We don't actually know our own username...and we need it to add a Watcher. So we get it from the
                            // "current user" resource
                            AJS.$.getJSON(base + '/rest/auth/latest/session', function(data) {
                                AJS.$.ajax({
                                    type: "POST",
                                    url: base + "/rest/api/latest/issue/" + key + "/watchers",
                                    data: "\""+ data['name']+ "\"",
                                    dataType: "json",
                                    contentType: "application/json"
                                })
                            })
                        }).appendTo(contents);
                        contents.append("</form>");
                        showPopup()
                    })
                };
                AJS.InlineDialog(AJS.$(this), "issue-linking-" + (i++), draw, options)
            }
        })
    })
});

require('issue-hover');

You can view the full demo app on Bitbucket.