My webhook isn't arriving or signature verification fails
Troubleshoot StableOps Webhook delivery and signature failures by checking endpoint settings, raw request bodies, signing secrets, retries, and replay logs.
Most webhook problems fall into two buckets: the delivery never reaches your server, or it reaches you but fails signature verification. Work through the relevant checklist below.
Deliveries aren't arriving
-
Is the endpoint subscribed to that event group? In the dashboard, subscriptions are chosen by group, not per event. There are three: Payment events (
payment_order.*,payment.*), Merchant subscription events (end_user_subscription.*,end_user_invoice.*), and Operational events (address.pool.lowand theagent.action.*approvals). An endpoint subscribed only to payment events won't receive subscription or operational/agent events, and vice versa. Leaving all three unchecked isn't allowed; checking all three means "all events", including any added in the future. (The API'senabled_eventsfield can technically carry an arbitrary subset, but the dashboard's group model can't represent an arbitrary subset. Saving a custom subset there widens it to the whole group.) -
Is your server returning
2xxwithin 10 seconds? Any non-2xxstatus, a network error, or no response within 10 s counts as a failure. A slow handler that does real work before responding is a common cause. Return200first, then process asynchronously. -
Check the delivery log. Each attempt records
response_status,response_duration_ms,error_message,attempts,next_retry_at, andstatus(pending/succeeded/failed/dead_letter). This usually tells you immediately whether the problem is on your side. -
Account for retries. Failed deliveries retry on this curve, then move to the dead letter queue on attempt 6:
Attempt Delay 1 30 s 2 60 s 3 5 min 4 30 min 5 2 h 6+ DLQ -
Replay once it's fixed. The replay endpoints (and the dashboard's replay button) enqueue a brand-new delivery row; the original audit log is preserved. Use replay-dead-letters to drain anything that hit the DLQ while your endpoint was down.
Signature verification fails
-
Verify against the raw body. Compute the signature over the exact bytes you received, before any JSON parsing or re-serialization. Frameworks that parse and re-stringify the body will change it and break the signature. This is the single most common cause.
-
Read the signature from the right header. Use the
SIGNATURE_HEADERconstant from the SDK rather than hardcoding the name. -
Use the correct secret. The
secretis shown only on create and rotate. If you didn't store it, rotate to get a new one. -
During rotation, accept both secrets. After you rotate, the previous secret stays valid alongside the new one for 24 hours. Pass both to the verifier so in-flight deliveries don't fail mid-rollout:
import { SIGNATURE_HEADER, verifySignature } from '@stableops/api-sdk/webhooks' const result = verifySignature({ secrets: [currentSecret, previousSecret], // both valid during the 24h overlap header: request.headers.get(SIGNATURE_HEADER) ?? undefined, rawBody, }) if (!result.ok) { console.error('Invalid signature:', result.reason) // Reject the request }
Related
How is this guide?
Last updated