Skip to content

Bitbucket webhook setup

Notifycat receives Bitbucket webhook requests at:

POST /webhook/bitbucket

This route is only registered when git_provider: bitbucket is set in config.yaml. Bitbucket must send JSON payloads and sign them with the same secret you set in BITBUCKET_WEBHOOK_SECRET. See Configuration → git_provider for the provider switch and Mappings for how Bitbucket workspaces and repository slugs map to Slack channels.

Creating the webhook

First, generate the webhook secret — exactly as described in Generating the webhook secret:

openssl rand -base64 32

The output becomes BITBUCKET_WEBHOOK_SECRET, and the same value goes into the webhook's Secret field below.

There is no creation script for Bitbucket webhooks (unlike GitHub). Create the webhook in the repository settings:

  1. Open the Bitbucket repository.
  2. Go to Repository settings.
  3. Open Webhooks.
  4. Click Add webhook.
  5. Give it a descriptive title (e.g. Notifycat).
  6. Set URL to your public Notifycat URL:
https://notifycat.example.com/webhook/bitbucket
  1. Set Secret to the value you generated above.

Secret is mandatory. If you leave the secret field blank, Bitbucket sends no X-Hub-Signature header. Notifycat hard-requires a valid signature and will reject every unsigned delivery with 401. The secret must be the same value you set in BITBUCKET_WEBHOOK_SECRET.

  1. Under Triggers, choose Choose from a full list of triggers and, under Pull request, enable the following events (the labels below are exactly what you tick in the Bitbucket UI):
UI label (Pull request) Event key (X-Event-Key) Purpose
Created pullrequest:created New PR opened.
Updated pullrequest:updated PR details changed, and also draft↔ready transitions (Bitbucket has no distinct draft event).
Changes Request created pullrequest:changes_request_created Reviewer requested changes.
Approved pullrequest:approved Reviewer approved.
Merged pullrequest:fulfilled PR merged.
Declined pullrequest:rejected PR declined/closed.
Comment created pullrequest:comment_created Comment posted on the PR.

Heads up on two labels. Bitbucket names two of these differently in the UI than on the wire: ticking Merged delivers pullrequest:fulfilled, and Declined delivers pullrequest:rejected. Notifycat keys on the event key (the X-Event-Key value you see in request history and logs), so seeing fulfilled/rejected there for a merged/declined PR is expected, not a bug.

  1. Keep Active checked.
  2. Save the webhook.

Bitbucket sends a test ping after creation; you can ignore it. Use Bitbucket's request history view (below) to inspect real PR deliveries after you open or update a pull request.

Access token & scopes

BITBUCKET_TOKEN is optional but required for two capabilities: per-path routing (reading a PR's changed files to select a path channel) and validate/reconcile probes (checking whether the webhook subscribes to the right events). Without it, path rules are inert and those probes are skipped — behavior identical to how GITHUB_TOKEN degrades on a GitHub deployment.

Create a repository access token or a workspace access token in Bitbucket with the following least-privilege scopes:

Scope Why
repository Read repository metadata and changed files.
pullrequest Read PR details and file lists.
webhook Read webhook configuration (for notifycat-config validate).

Set the token value in .env:

BITBUCKET_TOKEN=your-access-token

Leave BITBUCKET_AUTH_EMAIL unset to use Bearer authentication (the default for access tokens).

Free-plan fallback: scoped Atlassian API token

If your plan does not support repository access tokens, use a scoped Atlassian API token with HTTP Basic auth instead. Create the token at id.atlassian.com with the following read-only scopes:

Scope Why
read:repository:bitbucket Read repository metadata and changed files.
read:pullrequest:bitbucket Read PR details.
read:webhook:bitbucket Read webhook configuration.

Scoped API tokens have a maximum lifetime of 365 days. Set both variables in .env:

BITBUCKET_TOKEN=your-atlassian-api-token
BITBUCKET_AUTH_EMAIL=you@example.com

When BITBUCKET_AUTH_EMAIL is set, the client sends HTTP Basic email:token instead of Bearer.

Getting 401 on validation or reconcile? A 401 (as opposed to 403) means the auth scheme doesn't match the token type — not a scope problem. The two token types use different schemes, and mixing them is the most common cause:

Token type Auth scheme BITBUCKET_AUTH_EMAIL
Repository / workspace access token Bearer unset
Scoped Atlassian API token (Free-plan path) Basic set to your Atlassian email

An access token with an email set (forces Basic), or an API token with no email (falls back to Bearer), both produce 401. The token and email are read verbatim, so also check for stray quotes or whitespace in .env. To isolate it, replay the same call curl makes — a 200 from curl -H "Authorization: Bearer $BITBUCKET_TOKEN" https://api.bitbucket.org/2.0/repositories/<workspace>/<repo>/hooks means you want the Bearer/no-email combo.

App passwords — not supported

App passwords are not supported and are being removed by Atlassian on 2026-07-28. If your current setup uses a Bitbucket app password, migrate to a repository/workspace access token (Bearer) or a scoped Atlassian API token (Basic) before that date.

Signature verification

Bitbucket signs each delivery with HMAC-SHA256 over the raw request body and sends the result in the X-Hub-Signature header:

X-Hub-Signature: sha256=<hex-digest>

Notifycat verifies this header on every request before the JSON payload is parsed. Requests without a valid signature are rejected with 401. See Security & permissions for the full validation model.

A delivery failing with 401 has its runbook in Troubleshooting → Webhook returns 401invalid signature in Bitbucket's request history is exactly the secret mismatch it describes. To change the secret later, follow Rotating the webhook secret.

Delivery & troubleshooting

Bitbucket's webhook delivery behavior:

Property Value
Timeout per attempt 10 seconds
Retry attempts on 5xx or timeout Up to 3 total
Attempt number header X-Attempt-Number (1 on the first try)
Maximum payload size 256 KB (Notifycat's 1 MiB body guard covers it)

To inspect past deliveries, open the Bitbucket repository's Repository settings → Webhooks → View requests (or the equivalent in your Bitbucket version). Each entry shows the request headers, payload, response status, and response body. What you find there picks the runbook — start at Troubleshooting → No message for a new PR.

Optional IP allowlisting

For defense in depth, you can restrict inbound traffic to Bitbucket's delivery IP ranges. Atlassian publishes the current list at:

https://ip-ranges.atlassian.com

Allowlisting these ranges at your reverse proxy or firewall means only Bitbucket (and your own testing traffic) can reach /webhook/bitbucket. This is optional — signature verification provides the same authenticity guarantee — but it limits the attack surface if an attacker can reach your endpoint.