Webhook troubleshooting: my webhook isn't firing
Walk the chain end-to-end
When a webhook seems "broken", the failure is almost always at one of four specific points. Work through them in order.
1. Is the webhook enabled and subscribed to the right event?
- Open Developer → Webhooks and confirm the endpoint shows as Active (not paused or disabled).
- Click into the webhook and check the Events list. A webhook subscribed only to
wish_createdwill never fire onsupport_ticket_reply. - For org-scoped events (billing), make sure you created the webhook at the organization level (Organization → Developer → Webhooks), not under a single project.
2. Did the event actually happen?
- Some events look intuitive but don't fire on every UI action. For example, editing a ticket's category does not emit
support_ticket_reply— only an actual reply does. - Check the Delivery log tab on the webhook. If nothing is listed for the time window, the event didn't fire on our side and there's nothing to debug downstream.
3. Did the delivery attempt succeed?
The delivery log shows each attempt with its HTTP status code:
- 2xx — success. If your handler ran but didn't do anything, the bug is in your code, not the webhook.
- 4xx — your endpoint rejected the request. Common causes: signature verification failure, wrong content-type handling, auth middleware blocking requests without a user session.
- 5xx or timeout — your server errored or took longer than 10 seconds. AppGram will retry with exponential backoff (8 attempts over ~33 hours), then give up.
- DNS / TLS error — the hostname doesn't resolve or your certificate isn't valid. Self-signed certs are rejected.
4. Is signature verification rejecting valid deliveries?
This is the most common 4xx cause. Check:
- You're using the raw request body bytes, not a re-serialized JSON object. Even one whitespace difference produces a different HMAC. In Express, use
express.raw({ type: 'application/json' })on the webhook route only. - You're using the secret from this webhook, not another one. Each webhook has its own secret.
- You haven't regenerated the secret recently. If you have, deliveries in flight may still be signed with the old one.
- You're including the timestamp in the signed payload:
HMAC_SHA256(secret, timestamp + "." + rawBody). Forgetting the timestamp prefix is a silent failure. - You're using a constant-time comparison (e.g.
crypto.timingSafeEqual), not===. With===, the strings may legitimately not match because of buffer/string encoding differences.
5. Use "Send test event"
On the webhook detail page, click Send test event. It posts a synthetic payload with valid signatures to your endpoint:
- If the test succeeds but real events fail → your handler logic has an event-type-specific bug. Log
event.eventat the top of the handler. - If the test fails the same way real events do → the problem is in your endpoint setup (TLS, auth middleware, signature verification), not the event itself.
6. Still stuck?
Open a support ticket from your project with:
- The webhook ID (visible in the URL of the webhook detail page).
- A specific attempt ID from the delivery log that should have succeeded.
- Your endpoint's response status and body for that attempt (already in the log, but mention if you have additional context like server logs).
We can see deliveries from our side and reproduce the failure faster with that info.