Verify webhook signatures against the raw request body bytes, before any JSON parsing happens. Parsing and re-serializing changes whitespace, key order, and number formatting, so a signature computed over the parsed form can never match what the provider signed. Capture the raw bytes first, verify against them, then parse.

```text
agent's webhook signature check failed in production  -  it signed the parsed JSON instead of the raw body bytes
```

## Steps

1. In the webhook handler, capture the raw body as bytes before the framework parses it. Most frameworks offer a raw-body option or let you read the request stream first and parse a copy afterward.
   Expected: The handler holds the exact bytes that arrived on the wire, untouched by parsing.

2. Compute the HMAC over those raw bytes using the webhook signing secret, following the provider's documented algorithm and header format.
   Expected: The computed signature is derived from the raw bytes, not from re-serialized JSON.

3. Compare the computed signature against the signature header using a constant-time comparison, so timing differences cannot leak information about the secret.
   Expected: The comparison is constant-time and uses the documented header.

4. Redeploy and send a test webhook from the provider dashboard. Confirm verification passes and production deliveries stop failing.
   Expected: Test webhooks verify successfully and production deliveries stop failing signature checks.

## Use this when

- webhook signature verification fails on every delivery
- the handler parses JSON before verifying the signature
- the signature is computed over re-serialized or pretty-printed JSON

## Not for this skill when

- verification fails even with raw bytes - check whether the wrong secret is in use
- the digest encoding mismatches, for example hex compared against base64
- signatures pass but deliveries fail on timestamp checks - that is a tolerance problem

## Variant phrasings

### webhook signature mismatch raw body
### HMAC verification fails webhooks
### signed parsed JSON instead of raw body
### webhook signature check failed production

## Why it happens

The provider signs the exact bytes on the wire. JSON parsing is lossy for signature purposes: whitespace gets normalized, key order can change, and number formatting may shift on re-serialization. The parsed object is semantically identical but byte-different, so the HMAC never matches. It fails on every single delivery, which at least makes it unmistakable once you know the cause.

## Edge cases

- Frameworks that parse the body before your code runs need their raw-body option enabled - check the framework docs
- Chunked transfer encoding still yields the same body bytes after reassembly, so it does not break verification
- Log the raw body bytes for debugging, never the parsed form with secrets, and never log the signing secret
- Some providers sign a timestamp plus the body concatenated - read the exact signed payload construction in their docs

## Provenance

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