Signing — HMAC-SHA256
LMN signs every webhook with HMAC-SHA256 using a shared secret (Stripe-compatible scheme). Headers sent with each request:What’s signed
The signed payload is:<timestamp>is the value ofX-LMN-Timestamp(Unix seconds as string)..is a literal period.<raw_request_body_bytes>is the exact bytes of the HTTP body — do not parse and re-stringify the JSON.
Signature header format
v1= prefix leaves room for future signature schemes (v2=, …) without breaking existing clients.
The header can carry more than one v1 entry. During the 1-hour grace window after a production secret rotation, we sign each delivery with both the new and the old key — new key first, old key last:
v1 entry that matches the secret you hold. See Secret rotation.
Verification (Node.js)
Replay protection
Track received
event_ids for at least 24 hours and reject duplicates. A simple Redis set with 24h TTL works.
Clock skew tolerance: 5 minutes. NTP-sync your servers if drift exceeds that.
Retry policy (5 attempts)
Stops on
2xx (success) or 410 Gone (you say “stop sending”). After 5 failures, the event is marked delivery_failed — you can re-attempt it yourself from the console once your endpoint is fixed, without contacting LMN. See Replaying a webhook.
Per-attempt timestamp. Each retry generates a fresh
X-LMN-Timestamp and re-signs with the new timestamp — so the 5-minute replay window applies to the delivery time, not the original event. A retry at +12h is still verifiable.Secret rotation
Rotate your own webhook signing secret from the partner console — Webhooks → Production (or Sandbox). The new secret is shown once, at rotation time: LMN stores no readable copy and cannot show it to you again. Lose it and your only recourse is to rotate again. Production rotation is deliberately hard to trigger by accident. It requires all three of:
LMN staff can also rotate on your behalf if you ask — that path is an immediate cutover with no grace window, so it’s coordinated with you first.
The 1-hour dual-signature grace window
A rotation you run yourself against production does not cut over instantly. For 1 hour afterwards, every delivery is signed with both keys andX-LMN-Signature carries two v1 entries — the new key first, the old key last:
v1 entry, signed with the new key only.
No change on your side is required to survive a rotation. A verifier still holding only the old secret keeps passing for the full hour, including one using the single-entry
Object.fromEntries parser — that parser takes the last v1 entry, which is the old key’s signature. You have the hour to deploy the new secret.What to do, by parser
That second row is the reason to upgrade to the any-match parser: it decouples your deploy from our clock.
Sandbox has no grace window — a sandbox rotation is an immediate cutover. Nothing live depends on it, and it’s the environment to rehearse your handling in.