Bitbucket Cloud webhooks have one trap that catches almost everyone: the delivery log is off by default. Until you switch it on, Bitbucket records nothing — so when a webhook misbehaves, you're debugging blind and it feels like events simply vanish. Start there, then work down this list.
Open Repository settings → Webhooks → View requests and select Enable history. From that point on Bitbucket logs every delivery; use Load new requests to refresh the list. Each row shows the triggering event, the request time, and the outcome:
View details on a row shows the exact request headers and body Bitbucket sent and the response your server gave back. Two quirks worth knowing: a client timeout appears as response status -1 in the details view, and if your server never managed to respond, the Response tabs simply don't appear.
Because history is opt-in, anything that failed before you enabled it is
gone. If you want a delivery record that's always on, point the webhook at a capture
bin (button below, or curl https://hookden.pages.dev/new) — every request is stored
with full headers and raw body, and you can replay any of them at your real endpoint
once it's fixed.
Bitbucket gives your endpoint 10 seconds to respond. Slower than
that and the delivery is logged as TIMEOUT — so acknowledge with a quick
200 first and do expensive work after responding, not inline.
If your endpoint answers with a 5xx status, Bitbucket
automatically resends the request up to two more times. The
X-Attempt-Number header on each request (and the Request
attempts count in the log details) tells you which attempt you're looking at —
and it means your handler must tolerate duplicates, because attempt 2 can arrive
after attempt 1 actually succeeded but responded too slowly.
< > ^. If you use basic-auth
credentials in the URL and the username contains an @, percent-encode it
(someone%40somewhere.com:pass@host.com) or delivery breaks.To split "Bitbucket never sent it" from "my server never received it": point the webhook at a capture bin for a minute and trigger the event. If the delivery shows up in the bin, Bitbucket's side is fine and the problem is your endpoint, DNS, TLS, or firewall. If nothing arrives, the problem is the webhook config or a silent payload-size failure.
When you set a Secret on a Bitbucket Cloud webhook, each delivery
carries an X-Hub-Signature header in WebSub's
method=signature form — currently sha256=<hex>, an
HMAC-SHA256 of the raw request body using your secret. Three classic
failure modes:
X-Hub-Signature is
the legacy sha1 header and sha256 lives in
X-Hub-Signature-256. Bitbucket uses the unsuffixed header with
sha256=. Code copied from a GitHub verifier that assumes sha1 will never
match. Parse the method= prefix instead of assuming.JSON.stringify(req.body), key order and whitespace differ and
verification fails — same pitfall as Stripe and WooCommerce. Use the raw body.Capture one delivery in a bin, paste the secret into the bin's signature settings (scheme: generic HMAC over raw body), and the ✓/✗ badge tells you whether the header matches the raw bytes — which side of the handshake is lying.
Deliveries to an HTTPS endpoint with a self-signed certificate fail TLS verification. Bitbucket offers a Skip certificate verification option per webhook; it works, but it removes the guarantee that payloads reach your server, so treat it as a last resort and prefer a real certificate.
http://localhost:3000/hook isn't reachable from Bitbucket's cloud.
Instead of running a tunnel, use a capture URL as the webhook target and relay
deliveries to your machine:
$ curl -s https://hookden.pages.dev/cli -o hookden && chmod +x hookden $ curl https://hookden.pages.dev/new $ ./hookden relay YOUR_BIN http://localhost:3000
Bodies are re-delivered byte-identical, so the X-Hub-Signature HMAC
still verifies against the raw bytes Bitbucket signed. Details:
webhooks to localhost without a tunnel.
X-Attempt-Number header)X-Hub-Signature: sha256=<hex HMAC of raw body>Same problem on a different host? See GitHub webhook not working and GitLab webhook not working.
No signup needed. Or from your terminal: curl https://hookden.pages.dev/new