## TL;DR
Signature verification fails for boring reasons almost every time: the signing secret on your endpoint does not match the one on the webhook target, or you are verifying the parsed JSON body instead of the raw bytes Zendesk signed. Verify against the raw request body with the exact secret from the target settings, and allow a few minutes of timestamp skew.

## The query

```text
zendesk webhook "signature verification failed" on ticket events
```

## Use this when

- Your endpoint rejects Zendesk webhooks with signature failures
- Verification passes in local tests but fails in production
- A webhook that verified yesterday fails today
- You are building the receiving end of Zendesk ticket webhooks

## Not for

- Webhooks not firing at all (delivery problem)
- Trigger or automation logic design
- Webhook target URL configuration
- Retry and dead-letter handling

## Steps

### 1. Re-copy the signing secret from the webhook target

Secrets get rotated, and staging and production targets have different ones. Open the target settings, copy the current secret directly, and compare against what your endpoint uses. Never trust a secret pasted months ago.

Expected output: the endpoint secret matches the target secret character for character.

### 2. Verify against the raw request body

Zendesk signs the raw bytes it sent. If your framework parses the JSON first and you re-serialize it for verification, whitespace or key ordering differences break the signature. Capture the raw body before any parsing and verify against that.

Expected output: verification computed over raw bytes, not re-serialized JSON.

### 3. Check the signature header name and scheme

Confirm you are reading the correct header and applying the right algorithm (HMAC with the documented hash). A header-name typo means you verify an empty string and fail every time.

Expected output: correct header read, correct HMAC scheme applied.

### 4. Allow timestamp tolerance

If the signature scheme includes a timestamp, allow several minutes of clock skew between Zendesk and your server. An exact-match timestamp check fails on any drift.

Expected output: tolerance window configured, server clock checked.

### 5. Replay a known-good payload to isolate the layer

Sign a captured payload yourself with the target secret and verify it locally. If your own signature verifies, the problem is secret or transport; if it does not, the problem is your verification code.

Expected output: self-signed payload verifies, narrowing the fault to secret or transport.

## Template: the checklist

```text
Zendesk webhook signature failure:
[ ] Secret re-copied from the live webhook target
[ ] Verifying the raw request body, before parsing
[ ] Correct signature header and HMAC scheme
[ ] Timestamp tolerance allows clock skew
[ ] Self-signed replay verifies locally
```

## Variant phrasings

### zendesk webhook hmac invalid signature

Steps 1 and 2. Secret mismatch and raw-body signing.

### webhook signature fails only in production

Steps 1 and 4. Wrong-environment secret or clock skew.

### verify zendesk webhook signature example

Full sequence as the implementation checklist.

## Why it happens

HMAC verification is exact by design: one wrong byte anywhere in the secret, the body, or the scheme fails the check. The two failure modes that waste the most time are verifying a re-serialized body (which is never byte-identical to what was signed) and a stale secret after rotation, both of which look like a platform bug but are local config.

## Edge cases

- Proxies or CDNs that modify the body (compression, re-encoding) invalidate signatures. Verify at the edge before transformation.
- Multiple webhook targets sharing one endpoint need per-target secrets. Route by a target identifier before verifying.
- Secret rotation windows: support old and new secrets briefly during rotation instead of a hard cutover.
- URL-encoded versus raw bodies on form posts: sign what was actually transmitted.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_vq09P57acLG32ocm5hbcxQ
