StableOps
FAQ

My webhook isn't arriving or signature verification fails

A checklist for debugging missing webhook deliveries and signature verification errors.

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

  1. 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.low and the agent.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's enabled_events field 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.)

  2. Is your server returning 2xx within 10 seconds? Any non-2xx status, 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. Return 200 first, then process asynchronously.

  3. Check the delivery log. Each attempt records response_status, response_duration_ms, error_message, attempts, next_retry_at, and status (pending / succeeded / failed / dead_letter). This usually tells you immediately whether the problem is on your side.

  4. Account for retries. Failed deliveries retry on this curve, then move to the dead letter queue on attempt 6:

    AttemptDelay
    130 s
    260 s
    35 min
    430 min
    52 h
    6+DLQ
  5. 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

  1. 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.

  2. Read the signature from the right header. Use the SIGNATURE_HEADER constant from the SDK rather than hardcoding the name.

  3. Use the correct secret. The secret is shown only on create and rotate. If you didn't store it, rotate to get a new one.

  4. 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
    }

How is this guide?

Last updated

On this page