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
/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
)JQL filtering supported.
comment_created
)comment_updated
)comment_deleted
)JQL filtering supported.
attachment_created
)attachment_deleted
)JQL filtering supported.
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*filter_created
)filter_updated
)filter_deleted
)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
)option_timetracking_provider_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:
{attachment.id}
{board.id}
{comment.id}
{issue.id}
{issue.key}
{mergedVersion.id}
{modifiedUser.accountId}
{modifiedUser.key}
(deprecated){modifiedUser.name}
(deprecated){project.id}
{project.key}
{property.key}
{sprint.id}
{version.id}
{worklog.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 2https://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 2https://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 attempt following the failure is delayed with a randomized back-off algorithm and can happen between 5 and 15 minutes after the previous one.
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.
After we send a webhook and receive a failure, we log the time of failure. Every successful response from the recipient removes that log entry. However, if the recipient doesn’t respond successfully within 30 minutes, we only try to deliver a single attempt per webhook until we record a successful delivery.
Webhooks that are not delivered after the maximum number of retries can be found with the Get failed webhooks operation.
Each webhook contains X-Atlassian-Webhook-Flow
header with Primary
or Secondary
value.
All Primary
webhooks should be delivered within 30 seconds.
All Secondary
webhooks are a result of long-lasting bulk or cascade operation (bulk issue update, project deletion, issue deletion etc.).
For those webhooks, the expected delivery time requirements are relaxed, and the delivery should take no more than 15 minutes.
Note, in those cases, the top level webhook is transferred via Primary
flow, and all dependent webhooks are transferred using the Secondary
flow.
For example, when deleting an issue, the issue_deleted
event is transferred as Primary
but all dependent commend_deleted
, attachment_deleted
issuelink_deleted
etc. are Secondary
.
To make it easier for apps to process a large number of webhooks sent as a result of a sudden influx of events in Jira, a concurrency limiter is applied.
For Primary
webhooks, a default maximum number of 20
concurrent requests per tenant + webhook URL host
pair is allowed.
For Secondary
webhooks the default limit is 10
.
These limits can be changed without notice, depending on the tenant and the webhook host.
To trace the origin of a webhook, Connect apps can attach the additional X-Atlassian-Webhook-Trace
HTTP header with any value
consisting of a string of up to 1024 printable ASCII characters to a REST API request.
The header and its value are then attached to every webhook sent from Jira for the REST API request.
The app can use the webhook trace header to, for example:
In addition to declaring webhooks statically in the app descriptor, Connect and OAuth 2.0 apps can register webhooks dynamically using the REST API. This is useful where you need to observe issues based on how users define and amend the issues' content. For example, you may want to initiate other processing when an issue is given a certain label.
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 up to 3 months after they expire. It is not necessary to keep a Webhook alive during this period, you can refresh it when it is needed again.
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 app. An example request body is shown below:
1 2{ "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[ { "createdWebhookId": 1000 }, { "createdWebhookId": 1001 }, { "errors": ["The clause myClause is unsupported"] } ]
To receive a registered webhook, an app must be granted all the scopes for all the events in the webhook. This table shows the scopes required for each event.
Webhook type | Required scopes |
---|---|
jira:issue_created | read:issue-details:jira |
jira:issue_updated | read:issue-details:jira |
jira:issue_deleted | read:issue-details:jira |
comment_created | read:comment.property:jira , read:comment:jira , read:epic:jira-software , read:group:jira , read:issue.property:jira , read:issue-type:jira , read:project:jira , read:project-role:jira , read:status:jira , and read:user:jira |
comment_updated | read:comment.property:jira , read:comment:jira , read:epic:jira-software , read:group:jira , read:issue.property:jira , read:issue-type:jira , read:project:jira , read:project-role:jira , read:status:jira , and read:user:jira |
comment_deleted | read:comment.property:jira , read:comment:jira , read:epic:jira-software , read:group:jira , read:issue.property:jira , read:issue-type:jira , read:project:jira , read:project-role:jira , read:status:jira , and read:user:jira |
issue_property_set | read:epic:jira-software ,read:issue.property:jira ,read:issue-type:jira ,read:project:jira ,read:status:jira , and read:user:jira |
issue_property_deleted | read:epic:jira-software ,read:issue.property:jira ,read:issue-type:jira ,read:project:jira ,read:status:jira , and read:user:jira |
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{ "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{ ... usual webhook data ..., "matchedWebhookIds": [1000, 1001] }
An example Connect app that uses the REST API to register webhooks, fetch registered webhooks, delete them, and handle them, can be found here.
Webhooks for OAuth 2.0 apps are secured by bearer authentication. The token is present in the Authorization
header and is signed with the app's client secret.
Remember to verify the token to keep your integration secure. The use of a library to verify the token is recommended.
A list of suitable libraries can be found on the JWT website.
Starting from January 2, 2024, this type of integration will have a limit of 100 active admin webhooks.
To register a webhook using REST:
POST a webhook in JSON format to:
https://your-domain.atlassian.net/rest/webhooks/1.0/webhook
1 2{ "name": "my first webhook via rest", "url": "https://www.example.com/webhooks", "events": [ "jira:issue_created", "jira:issue_updated" ], "filters": { "issue-related-events-section": "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 (that is, delete) a webhook using 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 2curl --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 using 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 2curl --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 2curl --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, for example, 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{ "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{ "issue": { "id":"99291", "self":"https://your-domain.atlassian.net/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://your-domain.atlassian.net/rest/api/2/priority/3", "iconUrl": "https://your-domain.atlassian.net/images/icons/priorities/minor.svg", "name": "Minor", "id": "3" }, } }, "user": { "self":"https://your-domain.atlassian.net/rest/api/2/user?accountId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e", "accoundId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e", "accountType": "atlassian", "avatarUrls":{ "16x16":"https://your-domain.atlassian.net/secure/useravatar?size=small&avatarId=10605", "48x48":"https://your-domain.atlassian.net/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 bulk operation triggers the webhook, the field bulkOperationMetaData
is added to the webhook payload. For example:
1 2{ ... usual webhook data ..., "bulkOperationMetaData": { "sendMail": false } }
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.issue_deleted
webhooks.attachment_created
webhooks, but they are listed in attachment field in jira:issue_created
webhook’s body.Rate this page: