One problem · bounded diagnostic

Webhook signature mismatch: preserve the signed bytes

A present signature can still fail when the receiver verifies a different byte sequence, secret version or timestamp from the sender.

Keep the raw body for verification

Many providers sign the original request bytes. Parsing and reserializing JSON can change whitespace, key order or character encoding. Compare the verification input with the raw-body capture locally.

Check the provider contract

Confirm the signing algorithm, header format, timestamp tolerance and the configured secret version against your provider documentation. Never paste the secret or a live signed payload here.

Use a synthetic signed fixture

Create a fixture locally with a test secret. Check that the original body passes and a one-byte modification fails. Retain constant-time comparison and replay checks in the receiver.

Evidence to keep

Provider name, framework, middleware order and a sanitized failure description. Keep keys and customer payloads on your own system.

Scope of this check

The free probe reviews the symptom. It does not verify a real signature or trigger a delivery. Do not disable verification to make the error disappear.

Reproduce a signed-body mismatch locally

Synthetic Node.js example using a public test secret. This demonstrates byte sensitivity, not a provider-specific verifier. Provider header parsing, timestamps and replay protection require separate checks.

const {createHmac} = require("node:crypto");
const sign = body => createHmac("sha256", "public-test-secret").update(body).digest("hex");
const raw = '{"ok":true}';
console.log(sign(raw) === sign(raw));
console.log(sign(raw) === sign('{ "ok": true }'));

Expected output: true, then false. Equivalent JSON values can have different signed bytes. Preserve the original bytes required by your provider; never disable signature checks.

Need a scoped review? See the service scope and current price. Accept a quote only after reviewing its scope and available payment methods.

Related troubleshooting guides

Try the free probe

Use a sanitized symptom or a synthetic sample. The request goes to the existing NAIF diagnostic API. No payment or repair is started.

Review this service, current price and quote options

Availability is determined by the existing service and country policy. A diagnosis or quote does not confirm payment or guarantee a repair.

Use this guide with an AI agent
{
  "source": "discovery_pages_v1",
  "version": "2.0.0",
  "intentManifest": "/.well-known/naif-intents.json",
  "agentCard": "/.well-known/agent-card.json",
  "page_id": "webhook-signature-mismatch",
  "intent": "webhook_signature_mismatch",
  "productId": "webhook-debug-mini",
  "capability": "/api/agent/catalog",
  "pricing": "/api/agent/catalog",
  "probe": {
    "method": "POST",
    "endpoint": "/api/agent/probe",
    "body": {
      "productId": "webhook-debug-mini",
      "source": "discovery_pages_v1",
      "input": {
        "message": "A webhook includes the signature header, but verification fails after JSON middleware parses the request body."
      }
    }
  },
  "quote": "/api/agent/quote",
  "acceptQuote": "/api/quote/accept",
  "offers": "/.well-known/naif-offers.json",
  "checkout": "Use only the checkoutEndpoint returned by the existing quote acceptance API when checkoutEligible is true.",
  "attribution": "Retain the __Host-naif_discovery cookie, or echo X-Naif-Discovery-ID returned by this page on same-origin API requests. Do not treat the identifier as authentication."
}
Existing agent discovery