Skip to main content
Always verify the signature before trusting a webhook — it proves the delivery is genuinely from Numero and wasn’t tampered with or replayed.

The signature header

Each delivery carries:
  • t — the timestamp the signature was generated.
  • v1 — the signature.
The signed string is "{t}.{rawRequestBody}" — the t value, a literal dot, then the exact raw bytes of the request body.

How to verify

  1. Parse t and v1 from the X-Numero-Signature header.
  2. Build the signed string: `${t}.${rawBody}` (use the raw body, before JSON parsing).
  3. Compute HMAC-SHA256 of that string with your subscription’s signing secret, Base64-encode it.
  4. Compare to v1 using a constant-time comparison.
  5. Reject deliveries whose t is older than your tolerance (e.g. 5 minutes) to prevent replays.
Deprecated: a bare-body X-Webhook-Signature (HMAC-SHA256 of just the body, Base64) is still sent alongside X-Numero-Signature so older verifiers keep working. It carries no timestamp and so cannot detect a replay. Verify X-Numero-Signature instead; do not build new integrations on the bare-body header.

Code examples

Node.js

Python

C#

Important

  • Verify against the raw request body — before parsing it to JSON.
  • Always use a constant-time comparison (timingSafeEqual / compare_digest / FixedTimeEquals).
  • Enforce the timestamp tolerance so an intercepted delivery can’t be replayed later.
  • Use the envelope id to dedupe — the same event may be delivered more than once.
  • Never skip verification in production.