Rate this page:
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.
A webhook in Jira is defined by the following information, which you need to provide when registering (i.e. creating) a new webhook:
/rest/webhooks/1.0/webhook
("project = TEST and fixVersion = future")
To register (i.e. create) a webhook, you can use any of the following methods:
sharedSecret
)The information in this section will help you configure a webhook, whether you are creating a new one or modifying an existing one.
The following events are available for Jira webhooks. The string in parentheses is the name of the webhookEvent
in the response.
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.
jira:issue_created
)jira:issue_updated
)jira:issue_deleted
)JQL filtering supported.
issue_property_set
)issue_property_deleted
)JQL filtering supported.
worklog_created
)worklog_updated
)worklog_deleted
)comment_created
)comment_updated
)comment_deleted
)JQL filtering supported.
attachment_created
)attachment_deleted
)issuelink_created
)issuelink_deleted
)project_created
)project_updated
)project_deleted
)project_soft_deleted
)project_restored_deleted
) project_archived
)project_restored_archived
)jira:version_released
)jira:version_unreleased
)jira:version_created
)jira:version_moved
)jira:version_updated
)jira:version_deleted
)jira:version_deleted
)
note, this is the same webhook event name as the 'deleted' event, but the response will include a mergedTo
property*user_created
)user_updated
)user_deleted
)option_voting_changed
)option_watching_changed*
)option_unassigned_issues_changed
)option_subtasks_changed
)option_attachments_changed
)option_issuelinks_changed
)option_timetracking_changed
)sprint_created
)sprint_deleted
)sprint_updated
)sprint_started
)sprint_closed
)board_created
)board_updated
)board_deleted
)board_configuration_changed
)jira_expression_evaluation_failed
)Read more about monitoring Jira expressions here: Monitoring Jira expressions.
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.
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
Note that variables are only available to a webhook in the context of its registered events. For example, {issue.key}
and {issue.id}
are available to webhooks registered for events related to issues.
If a webhook is sent to its callback URL but fails, Jira Cloud will attempt to resend it up to five times. Each subsequent attempt is delayed with a randomized exponential back-off algorithm. The first retry occurs after at least 10 seconds, and the last between 15 and 30 minutes from the first failure.
Retries are attempted when any of the following are true:
This means that some webhooks might be delivered more than once (if the delivery acknowledgment fails).
Every webhook contains the X-Atlassian-Webhook-Identifier
header that provides an identifier for a webhook.
This identifier is unique within a Jira Cloud tenant and is the same across retries.
After you have processed a webhook, you can use the identifier to filter out retries.
The X-Atlassian-Webhook-Retry
header with the current retry count is included with webhooks that have been retried.
Monitor this header and cross-reference it with the callback server logs to stay on top of any unexpected reliability problems.
Webhooks that are not delivered after the maximum number of retries can be found with the Get failed webhooks operation.
To trace the origin of a webhook, Connect apps attach the X-Atlassian-Webhook-Trace
HTTP header with a value
consisting of a string of up to 255 printable ASCII characters to a REST API request.
The header and its value is then attached to every webhook sent from Jira for the REST API request.
The app then uses the webhook trace header to, for example:
In addition to declaring webhooks statically in the app descriptor, Connect apps can register webhooks dynamically using the REST API. This is perfect if you need to observe a set of issues that is not known beforehand, but instead configured by users at runtime.
Limitations apply to the REST API in order to guarantee stability and performance of Jira Cloud for all apps and users.
The following subset of webhook events is supported:
jira:issue_created
jira:issue_updated
jira:issue_deleted
comment_created
comment_updated
comment_deleted
issue_property_set
issue_property_deleted
These are all events that allow JQL filtering. Webhooks for other events can be registered in the app descriptor.
Only a subset of JQL clauses is supported. This enables us to optimize event matching on the Jira side.
Supported clauses (left-hand side values):
issueKey
project
issuetype
status
assignee
reporter
issue.property
(any indexed issue property can be queried)cf[id]
(for custom fields – currently only the ID of the Epic custom field is supported here)Supported operators: =
, !=
, IN
, and NOT IN
.
If you would like to use more clauses or operators, raise a request in the Atlassian Connect in Jira project or watch and comment on a relevant issue there.
In order to avoid sending webhooks no one listens to anymore, we assign each webhook an expiration date after which the webhook becomes inactive. The expiration period is 30 days from the time the webhook was created or refreshed using the Extend webhook life REST resource.
Webhooks are available for retrieval for at least 3 months after they expire. Thanks to this, you can wait with refreshing your webhooks until they are needed again; it's not necessary to keep them alive at all times.
To register webhooks through the REST API, make a POST
request to https://your-domain.atlassian.net/rest/api/2/webhook
. The registered URL must use the same base URL as the Connect app. 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": "myClause = 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 6 7 8 9 10 11
[
{
"createdWebhookId": 1000
},
{
"createdWebhookId": 1001
},
{
"errors": ["The clause myClause is unsupported"]
}
]
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.
To delete webhooks registered through the 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.
Webhooks registered with the REST API expire after 30 days. Because of that, it's necessary to periodically call the Extend webhook life API to keep them alive. Each call to the API extends the expiration date by another 30 days.
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]
}
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.
To register a webhook via REST:
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
}
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
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:
Notes:
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.
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.
Every callback contains the webhookEvent
ID, timestamp
, and information about the entity associated with the event (for example issue, project, or board). The callback can have additional information, depending on the type of event associated with it.
For example, issue-related events contain the issue_event_type_name
field. This field indicates the Jira event that triggered the callback.
This is the structure of a callback for an issue-related event:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
{
"timestamp",
"webhookEvent",
"issue_event_type_name",
"user": {
--> See User shape in table below
},
"issue": {
--> See Issue shape in table below
},
"changelog" : {
--> See Changelog shape in table below
}
}
Issue shape |
|
User shape |
|
Changelog shape |
|
Comment shape |
|
The returned fields depend on the webhook type. The following is an example of the JSON sent in an issue update callback:
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
{
"issue": {
"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": {
"self": "https://jira.atlassian.com/rest/api/2/priority/3",
"iconUrl": "https://jira.atlassian.com/images/icons/priorities/minor.svg",
"name": "Minor",
"id": "3"
},
}
},
"user": {
"self":"https://jira.atlassian.com/rest/api/2/user?accountId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
"accoundId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
"accountType": "atlassian",
"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",
"timeZone": "Europe/Warsaw",
},
"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
},
"timestamp": 1606480436302,
"webhookEvent": "jira:issue_updated",
"issue_event_type_name": "issue_generic"
}
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.
issue_created
event instead.Rate this page: