Last updatedAug 25, 2017

Webhooks

A webhook is a user-defined callback over HTTPS. You can use Jira webhooks to notify your app or web application when certain events occur in Jira. For example, you might want to alert your remote application when an issue has been updated or when sprint has been started. Using a webhook to do this means that your remote application doesn't have to periodically poll Jira (via the REST APIs) to determine whether changes have occurred.

Overview

A webhook in Jira is defined by the following information, which you need to provide when registering (i.e. creating) a new webhook:

  • a name for the webhook created ("Webhook for my remote app")
  • the URL where the callback should be sent (only secure HTTPS URLs are allowed)
  • (optional) the scope of the webhook, for example either "all issues" or a limited set of issues specified by JQL ("project = TEST and fixVersion = future")
  • the events to post to the URL, either "all events" or a specific set of events

Registering a webhook

To register (i.e. create) a webhook, you can use any of the following methods:

Configuring a webhook

The information in this section will help you configure a webhook, whether you are creating a new one or modifying an existing one.

Available webhook events

The following events are available for Jira webhooks. The string in parentheses is the name of the webhookEvent in the response.

Restricting your events to a subset of issues via JQL

If you only want your webhook events to fire for a specific set of issues, you can specify a JQL query in your webhook to send only events triggered by matching issues. For example, you could register for all Issue Deleted events on issues in the Test ("TEST") Project with the Performance component.

JQL filtering is only supported by a subset of webhook events. Refer to the event sections below for details.

Note, although the JQL that uses standard Jira fields is very performant, some custom fields, because of their implementation, can take multiple seconds to query. You should take this into account when using JQL for a webhook, as the query will be run with each event.

Issue webhooks

  • created (jira:issue_created)
  • updated (jira:issue_updated)
  • deleted (jira:issue_deleted)

JQL filtering supported.

Worklog webhooks

  • created (worklog_created)
  • updated (worklog_updated)
  • deleted (worklog_deleted)

Note, callbacks for worklog events will soon be restricted to only include the 100 most recent items. See the notice.

Comment webhooks

  • created (comment_created)
  • updated (comment_updated)
  • deleted (comment_deleted)

JQL filtering supported.

Attachment webhooks

  • created (attachment_created)
  • deleted (attachment_deleted)
  • created (issuelink_created)
  • deleted (issuelink_deleted)

Project webhooks

  • created (project_created)
  • updated (project_updated)
  • deleted (project_deleted)

Version webhooks

  • released (jira:version_released)
  • unreleased (jira:version_unreleased)
  • created (jira:version_created)
  • moved (jira:version_moved)
  • updated (jira:version_updated)
  • deleted (jira:version_deleted)
  • merged (jira:version_deleted) note, this is the same webhook event name as the 'deleted' event, but the response will include a mergedTo property*

User webhooks

  • created (user_created)
  • updated (user_updated)
  • deleted (user_deleted)

Jira system configuration webhooks

  • voting (option_voting_changed)
  • watching (option_watching_changed*)
  • unassigned issues (option_unassigned_issues_changed)
  • subtasks (option_subtasks_changed)
  • attachments (option_attachments_changed)
  • issue links (option_issuelinks_changed)
  • time tracking (option_timetracking_changed)

Sprint webhooks

  • created (sprint_created)
  • deleted (sprint_deleted)
  • updated (sprint_updated)
  • started (sprint_started)
  • closed (sprint_closed)

Board webhooks

  • created (board_created)
  • updated (board_updated)
  • deleted (board_deleted)
  • configuration changed (board_configuration_changed)

Choosing events for your webhook

If you are unsure which events to register your webhook for, then the simplest approach is to register your webhook for all events for the relevant entity (e.g. all issue-related events). Then you won't need to update the webhook if you need to handle these events in future; you can just add code in your app or web application once you want to react to them.

Variable substitution in the webhook URL

You can append variables to the webhook URL when creating or updating a webhook. The variables are listed below:

  • ${board.id}
  • ${issue.id}
  • ${issue.key}
  • ${mergedVersion.id}
  • ${modifiedUser.accountId}
  • ${project.id}
  • ${project.key}
  • ${sprint.id}
  • ${version.id}

You can use these variables to dynamically insert the value of the current issue key, sprint ID, project key, etc, into the webhook URL when it is triggered. Consider the following example:

Say you specified the URL for your webhook with the ${issue.key} variable, like this:

1
https://service.example.com/webhook/${issue.key}

If an issue with the key EXA-1 triggers a webhook, then the URL that will be used is:

1
https://service.example.com/webhook/EXA-1

Retries

If the URL you have defined does not return a successful response to the webhook POST, Jira Cloud will retry sending three times. Currently there is no backoff or log available. If you are concerned about missed webhooks, we recommend occasionally polling Jira for recent updates.

Registering a webhook via the Jira REST API For Connect apps

This API allows Connect apps to register webhooks with JQL filters. This is a more flexible alternative to declaring webhooks statically in the app descriptor.

Restrictions

We want to provide a performant experience for apps and their users. This is why the following limitations apply to this API:

  • Only webhooks for the following events are supported:
    • jira:issue_created, jira:issue_updated, jira:issue_deleted, comment_created, comment_updated, comment_deleted.
  • Webhooks are only permitted to use a subset of JQL:
    • Supported clauses: issueKey, project, issuetype, status, assignee, reporter, issue.property, cf[id] (for custom fields).
    • Only the epic label custom field is supported at the moment.
    • Supported operators: =, !=, IN, and NOT IN.
  • Only one callback URL is allowed, per Connect app per tenant.
  • Only a limited number of webhooks are permitted, per Connect app, per tenant. If you exceed the limit, an error will be returned by the REST API when you create new webhooks. This limit is subject to change and may increase in future.

If you would like more clauses or operators to be supported, raise a request in the Atlassian Connect in Jira project or vote for a relevant issue there.

Important: While this is not the case now, in the future we may require apps to periodically ping Jira to renew registered webhooks, or they will be deleted automatically.

Using the REST API: Registration

To register webhooks through the REST API, make a POST request to https://your-domain.atlassian.net/rest/api/2/webhook. An example request body is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
   "url": "https://your-app-base-url.com/webhook-received",
   "webhooks": [
      {
         "jqlFilter": "project = PRJ AND status = Done",
         "events": ["jira:issue_created", "jira:issue_updated"]
      },
      {
         "jqlFilter": "status = Done",
         "events": ["jira:issue_updated"]
      },
      {
         "jqlFilter": "unsupportedClause = something",
         "events": ["jira:issue_deleted"]
      }
   ]
}

This request will try to register three webhooks. The response will be a JSON array with a result for every webhook. In this case, we will likely succeed in registering the first two, while the third one will fail due to the unsupported JQL clause. The response will then look like this:

1
2
3
4
5
[
   {"createdWebhookId": 1000},
   {"createdWebhookId": 1001},
   {"errors": ["The clause unsupportedClause is unsupported"]}
]

Using the REST API: Fetching registered webhooks

To fetch webhooks registered through the REST API, make a GET request to https://your-domain.atlassian.net/rest/api/2/webhook. This API uses pagination. The query parameters startAt and maxResults can be provided to specify the desired page, or the default values of 0 and 100 will be used.

Using the REST API: Deleting registered webhooks

To delete webhooks registered through this REST API, a DELETE request can be made to https://your-domain.atlassian.net/rest/api/2/webhook.

In your request, include the IDs of the webhooks to be deleted:

1
2
3
{
   "webhookIds": [1000, 1001]
}

IDs corresponding to non-existent webhooks are ignored.

Format of the webhook data sent by Jira

The format is the same as for webhooks declared in the Connect app descriptor, but with an additional property specifying which webhooks were matched for the event. For example, if an event happens in Jira that matches both JQL filters of the webhooks registered, which were successfully registered and assigned the IDs of 1000 and 1001, the webhook data JSON will contain the additional matchedWebhookIds root level property:

1
2
3
4
{
   ... usual webhook data ...,
   "matchedWebhookIds": [1000, 1001]
}

Example Connect app using this REST API

An example Connect app that uses the new REST API to register webhooks, fetch registered webhooks, delete them, and handle them, can be found here.

Registering a webhook via the Jira REST API Other integrations

To register a webhook via REST:

  1. POST a webhook in JSON format to: https://your-domain.atlassian.net/rest/webhooks/1.0/webhook

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     {
      "name": "my first webhook via rest",
      "url": "https://www.example.com/webhooks",
      "events": [
        "jira:issue_created",
        "jira:issue_updated"
      ],
      "jqlFilter": "Project = JRA AND resolution = Fixed",
      "excludeIssueDetails" : false
    }
  2. The response will return the webhook in JSON with additional information, including the user that created the webhook, the created timestamp, etc.

To unregister (i.e. delete) a webhook via REST:

  • Execute a DELETE to the following URL: https://your-domain.atlassian.net/rest/webhooks/1.0/webhook/{webhookId}

    The following would delete the webhook with an ID of 70:

    1
    2
    3
    4
    curl --user username:password \
        -X DELETE \
        -H "Content-Type: application/json" \
        https://your-domain.atlassian.net/rest/webhooks/1.0/webhook/70

To query a webhook via REST:

  • To get all webhooks for a Jira instance, perform a GET with the following URL: https://your-domain.atlassian.net/rest/webhooks/1.0/webhook.

    1
    2
    3
    4
    curl --user username:password \
        -X GET \
        -H "Content-Type: application/json" \
        https://your-domain.atlassian.net/rest/webhooks/1.0/webhook
  • To get a specific webhook by its ID, perform a GET with the following URL: https://your-domain.atlassian.net/rest/webhooks/1.0/webhook/{webhookId}

    The following would get a webhook with an ID of 72:

    1
    2
    3
    4
    curl --user username:password \
        -X GET \
        -H "Content-Type: application/json" \
        https://your-domain.atlassian.net/rest/webhooks/1.0/webhook/72

Adding webhooks to workflow post functions

Webhooks can be attached as a workflow post function. This makes it easy to trigger a webhook when an issue makes a workflow transition, for instance when it gets rejected from QA or when it gets resolved.

To add a webhook as a post function to a workflow:

  1. Configure your workflow and select the desired transition in the workflow designer. For more details, see Working with workflows.
  2. Click Post functions, then click Add post function.
  3. Select Trigger a Webhook from the list of post functions and click Add.
  4. Select the desired webhook from the list of webhooks and click Add to add the webhook as a post function.

Notes:

  • If the webhook you choose is also configured to listen to events, then the webhook will be triggered twice: once for the event and once for the workflow transition. For this reason, you should always unselect all events from the webhook admin screen.
  • If a webhook is associated with a post-function, you will not be able to delete the webhook. You must disassociate it from the post-function first.

Executing a webhook

Webhooks will be run without a specific user context, e.g. all issues will be available to the webhook, rather than having them scoped to a single user. (Note the URL will also contain user parameters in the form ?user_id=admin&user_key=admin appended at the end.)

By default, a webhook will send a request with a JSON callback when it is triggered. If you don't want the JSON body sent, then you will need to select Exclude body when configuring the webhook.

At a high level, every callback contains the webhookEvent ID, the timestamp, and information about the entity associated with the event (e.g. issue, project, board, etc). Callbacks may have additional information, depending on the type of event associated with it. As an example, the structure of a callback for an issue-related event is described below:

A callback for an issue-related event is structured like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
    "timestamp",
    "event",
    "user": {
        --> See User shape in table below
    },
    "issue": {
        --> See Issue shape in table below
    },
    "changelog" : {
        --> See Changelog shape in table below
    },
    "comment" : {
        --> See Comment shape in table below
    }
}

Issue shape
User shape
  • The same shape returned from the Jira REST API when a user is retrieved, but without the active, timezone, or groups elements.
  • The shape of the user returned is a condensed shape from the normal user API in REST, but similar to what is returned when the user is embedded in another response.
  • For the full user details, use the Jira REST API and query with the username.
  • REST API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-user-get
  • Example: https://your-domain.atlassian.net/rest/api/3/user?accountId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e
  • The user is always present in a webhook POST for issue events
  • The user includes an accountType field that is used to distinguish different types of users, such as normal users (atlassian), app users (app), and Jira Service Desk customers (customer).
  • Changelog shape
    • An array of change items, with one entry for each field that has been changed
    • The changelog is only provided for the `issue_updated` event
    • This is similar in format to the changelog you would retrieve from a Jira issue, but without the user (since that is already in the JSON) and without the created timestamp (since that is also already in the JSON for a webhook event)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    {
        "changelog": {
            "items": [
                {
                    "toString": "A new summary.",
                    "to": null,
                    "fromString": "What is going on here?????",
                    "from": null,
                    "fieldtype": "jira",
                    "field": "summary"
                },
                {
                    "toString": "New Feature",
                    "to": "2",
                    "fromString": "Improvement",
                    "from": "4",
                    "fieldtype": "jira",
                    "field": "issuetype"
                },
            ],
            "id": 10124
        }
    }
    Comment shape

    You can see a full example of this below. This JSON callback shows an update to an issue (some fields changed value) where a comment was also added:

    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
     {
        "id": 2,
        "timestamp": "2009-09-09T00:08:36.796-0500",
        "issue": {
            "expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog",
            "id":"99291",
            "self":"https://jira.atlassian.com/rest/api/2/issue/99291",
            "key":"JRA-20002",
            "fields":{
                "summary":"I feel the need for speed",
                "created":"2009-12-16T23:46:10.612-0600",
                "description":"Make the issue nav load 10x faster",
                "labels":["UI", "dialogue", "move"],
                "priority": "Minor"
            }
        },
        "user": {
            "self":"https://jira.atlassian.com/rest/api/2/user?accountId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
            "accoundId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
            "name":"brollins",
            "emailAddress":"bryansemail at atlassian dot com",
            "avatarUrls":{
                "16x16":"https://jira.atlassian.com/secure/useravatar?size=small&avatarId=10605",
                "48x48":"https://jira.atlassian.com/secure/useravatar?avatarId=10605"
            },
            "displayName":"Bryan Rollins [Atlassian]",
            "active" : "true"
        },
        "changelog": {
            "items": [
                {
                    "toString": "A new summary.",
                    "to": null,
                    "fromString": "What is going on here?????",
                    "from": null,
                    "fieldtype": "jira",
                    "field": "summary"
                },
                {
                    "toString": "New Feature",
                    "to": "2",
                    "fromString": "Improvement",
                    "from": "4",
                    "fieldtype": "jira",
                    "field": "issuetype"
                }
            ],
            "id": 10124
        },
        "comment" : {
            "self":"https://jira.atlassian.com/rest/api/2/issue/10148/comment/252789",
            "id":"252789",
            "author":{
                "self":"https://jira.atlassian.com/rest/api/2/user?accoundId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
                "name":"brollins",
                "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
                "emailAddress":"bryansemail@atlassian.com",
                "avatarUrls":{
                    "16x16":"https://jira.atlassian.com/secure/useravatar?size=small&avatarId=10605",
                    "48x48":"https://jira.atlassian.com/secure/useravatar?avatarId=10605"
                },
                "displayName":"Bryan Rollins [Atlassian]",
                "active":true
            },
            "body":"Just in time for AtlasCamp!",
            "updateAuthor":{
                "self":"https://jira.atlassian.com/rest/api/2/user?accountId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
                "accoundId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
                "name":"brollins",
                "emailAddress":"brollins@atlassian.com",
                "avatarUrls":{
                    "16x16":"https://jira.atlassian.com/secure/useravatar?size=small&avatarId=10605",
                    "48x48":"https://jira.atlassian.com/secure/useravatar?avatarId=10605"
                },
                "displayName":"Bryan Rollins [Atlassian]",
                "active":true
            },
            "created":"2011-06-07T10:31:26.805-0500",
            "updated":"2011-06-07T10:31:26.805-0500"
        },
        "timestamp": "2011-06-07T10:31:26.805-0500",
        "webhookEvent": "jira:issue_updated"
    }

    Response codes

    If a webhook is triggered successfully, a response code of 200 is returned. All other response codes should be treated as an unsuccessful attempt to trigger the webhook.

    Known issues

    • Post function web hooks will not fire if added to the Create Issue workflow transition. We recommend that you configure your web hook to fire from the issue_created event instead.