---
title: "Jira Webhooks: Setup, Payload and Security | WebhookRelay"
meta:
  "og:description": "How to set up Jira webhooks — admin UI vs REST API, JQL filters, the payload format, X-Hub-Signature verification and testing. A complete, practical guide."
  "og:title": "Jira Webhooks: Setup, Payload and Security"
  description: "How to set up Jira webhooks — admin UI vs REST API, JQL filters, the payload format, X-Hub-Signature verification and testing. A complete, practical guide."
---

![Stripes](https://webhookrelay.com/blog/jira-webhooks-guide/images/stripes.svg)

# **Jira Webhooks: Setup, Payload and Security**

How to set up Jira webhooks — admin UI vs REST API, JQL filters, the payload format, X-Hub-Signature verification and testing. A complete, practical guide.

![Jira webhooks guide](https://webhookrelay.com/blog/jira-webhooks-guide/images/blog/heroes/route.jpg)

Jira can notify your code the moment an issue is created, updated, transitioned or commented on. That is what **Jira webhooks** are: HTTP POSTs that Jira Cloud (or Data Center) sends to a URL you choose, carrying the issue as JSON. This guide covers the parts people actually get stuck on — the two ways to register a webhook, JQL filtering, what the payload really contains, and how to secure and test the whole thing.

If your immediate goal is receiving Jira events on `localhost`, we have a focused walkthrough for that: [Test Jira webhooks locally](https://webhookrelay.com/blog/jira-webhooks-guide/blog/receive-jira-webhooks-locally/). This guide is the broader reference.

## [Two ways to register a Jira webhook](#two-ways-to-register-a-jira-webhook)

**1. The admin UI (classic webhooks).** Go to **Settings → System → Webhooks → Create a WebHook**. You give it a name, a URL, select events (issue created/updated/deleted, comment events, project/version/user events, and more) and optionally a JQL filter. This is the fastest path and fine for internal tooling — but these webhooks are **not signed**, and they are instance-wide, so you need Jira admin rights.

**2. The REST API (dynamic webhooks).** Apps using OAuth 2.0 or Connect register webhooks with `POST /rest/api/3/webhook`. Dynamic webhooks support a **signing secret** (verification below), are scoped to the app that created them, and expire — Jira Cloud requires apps to refresh them periodically (they last 30 days unless extended), so schedule a refresh job.

There is a third, often-overlooked option: **Automation rules**. Jira's automation can fire a "Send web request" action on any rule trigger, which behaves like a webhook with full control over method, headers and body. If you need custom headers (say, an `Authorization` token) this is the easiest way to get them — classic webhooks send no custom headers.

## [Scope deliveries with JQL](#scope-deliveries-with-jql)

A webhook without a filter receives events for the whole instance. Add a JQL filter at registration time to narrow it:

```
project = PROJ AND issuetype = Bug
```

Only events for matching issues are delivered. This is the single best lever for keeping your handler simple — filter in Jira, not in code.

## [The payload](#the-payload)

Every delivery is JSON with a `webhookEvent` field. An issue update looks like this (trimmed):

```
{
  "timestamp": 1768471800000,
  "webhookEvent": "jira:issue_updated",
  "user": { "accountId": "5f00...aa", "displayName": "Alex Doe" },
  "issue": {
    "key": "PROJ-42",
    "fields": {
      "summary": "Checkout button unresponsive on mobile",
      "status": { "name": "In Progress" },
      "priority": { "name": "High" },
      "assignee": { "displayName": "Alex Doe" }
    }
  },
  "changelog": {
    "items": [
      { "field": "status", "fromString": "To Do", "toString": "In Progress" }
    ]
  }
}
```

Things worth knowing:

- **`changelog` is where the diff lives.** For `jira:issue_updated` it lists each changed field with `fromString`/`toString`. If you only care about status transitions, read the changelog instead of diffing the whole issue.
- **Comment events** carry a `comment` object alongside the issue.
- **Deliveries include tracing headers** — `X-Atlassian-Webhook-Identifier` uniquely identifies the delivery attempt, which is handy for idempotency and log correlation.
- Payloads can be large (full issue with all custom fields). Budget for tens of kilobytes.

Want to see the real thing without writing a handler? Open a free [Webhook Bin](https://webhookrelay.com/blog/jira-webhooks-guide/webhook-bin/), point a test webhook at it and trigger an event — or use the bin's sample catalog, which includes real-shaped `jira:issue_updated` and `jira:issue_created` events you can send to any endpoint with one click.

## [Securing Jira webhooks](#securing-jira-webhooks)

How you secure a Jira webhook depends on how it was created:

- **Dynamic webhooks (REST API) with a secret** are signed: Jira computes HMAC-SHA256 over the raw body and sends it as `sha256=…` in the **`X-Hub-Signature`** header. Recompute it with your secret and compare in constant time. You can sanity-check an implementation with the free [HMAC signature verifier](https://webhookrelay.com/blog/jira-webhooks-guide/hmac-verification/), and the general pitfalls (raw body, timing-safe compare) are covered in [Verify a webhook signature](https://webhookrelay.com/blog/jira-webhooks-guide/blog/verify-webhook-signature/).
- **Classic webhooks (admin UI) are not signed.** Mitigate with a secret token in the URL path or query string (`/hooks/jira?token=…`) that your handler checks, and/or restrict inbound traffic to [Atlassian's published IP ranges](https://support.atlassian.com/organization-administration/docs/ip-addresses-and-domains-for-atlassian-cloud-products/).
- **Automation "Send web request"** lets you set your own auth header — treat it like any API client.

## [Delivery behavior and reliability](#delivery-behavior-and-reliability)

Jira Cloud expects your endpoint to answer quickly with a 2xx. Slow or failing endpoints get their deliveries dropped — Jira's retry behavior is limited, so **do not treat webhook delivery as guaranteed**. The standard hardening pattern:

1. Return `200` immediately, queue the event, process asynchronously.
2. Deduplicate on `X-Atlassian-Webhook-Identifier` (retried attempts reuse context).
3. Reconcile periodically against the REST API for anything missed.

If you need stronger guarantees than Jira gives you, put a stable relay endpoint in front of your handler: Webhook Relay stores every delivery and [retries towards your destination for up to 30 days](https://webhookrelay.com/blog/jira-webhooks-guide/features/durable-retries/), so a redeploy or an outage on your side no longer loses events.

## [Testing your integration](#testing-your-integration)

The fast loop:

1. **Inspect first** — point the webhook at a [Webhook Bin](https://webhookrelay.com/blog/jira-webhooks-guide/webhook-bin/) and trigger real events to see exact headers and bodies.
2. **Develop locally** — forward events to your machine with `relay forward --bucket jira http://localhost:8080/webhook`; the [local testing guide](https://webhookrelay.com/blog/jira-webhooks-guide/blog/receive-jira-webhooks-locally/) walks through it.
3. **Replay** captured deliveries against your handler while you iterate, instead of re-triggering issues in Jira.

## [Related reading](#related-reading)

- [Test Jira webhooks locally](https://webhookrelay.com/blog/jira-webhooks-guide/blog/receive-jira-webhooks-locally/) — the localhost-focused walkthrough
- [Send webhooks _to_ Jira](https://webhookrelay.com/blog/jira-webhooks-guide/blog/webhook-to-jira/) — the reverse direction: creating issues from incoming webhooks
- [Confluence webhooks](https://webhookrelay.com/blog/jira-webhooks-guide/blog/confluence-webhooks/) — the same patterns on Jira's sibling
- [How to test webhooks](https://webhookrelay.com/blog/jira-webhooks-guide/blog/how-to-test-webhooks/) and [What is a webhook](https://webhookrelay.com/blog/jira-webhooks-guide/blog/what-is-webhook/)