Check in order: the delivery log, the event name, the firewall, the signature. The Deliveries list shows whether Didit sent it, halving the problem at once.
Start at the endpoint's Deliveries section. If Didit never attempted the delivery, it's a subscription or endpoint problem. If it attempted and failed, the response code tells you which: 404 means your route wasn't reachable at that URL, a signature mismatch means you hashed the wrong bytes.
#Step 1: did Didit try to send it?
Go to Developers → Webhooks. The list shows each endpoint's status, its success rate over 30 days and its last delivery; clicking a row shows its recent deliveries. Press Open for the endpoint's full Deliveries list, where every attempt is logged individually with the request and the response. Resend on a delivery sends it again once you've fixed your side. A failing endpoint is flagged at the top of the Webhooks page, and where available, a Retry failed button there resends every failed delivery at once.

That single check splits the problem in half:
- No attempt logged → the event was never generated for that endpoint. Go to step 2.
- Attempt logged, failed → Didit sent it and your side rejected or didn't receive it. Go to step 3.
- Attempt logged, 2xx → it was delivered successfully. The problem is inside your handler, not in delivery.
#Step 2: no attempt was logged
In order of likelihood:
- The event isn't subscribed. There's no wildcard - every event family has to be listed explicitly. And a name that doesn't exist (
session.status.updated,kyc.completed) silently receives nothing. Check against the event list. - Wrong application or environment. Endpoints belong to an application, and Live and Sandbox have separate endpoints: the list shows each endpoint's environment. If your sessions run under a different application than the endpoint, no event will ever reach it. This is the most common cause when everything looks correctly configured.
- Nothing actually changed. Webhooks fire on change. A session that hasn't moved, or an AML monitoring re-screen that found nothing above threshold, correctly produces no event.
- The endpoint is disabled. A disabled endpoint shows Disabled in the list and receives nothing until you enable it again.
- The status you're waiting for hasn't happened. A session at In progress isn't finished. See when a session never finishes.
#Step 3: the delivery was attempted and failed
A 404 means the request reached something that didn't have your route. Check:
- The exact path, including a trailing slash. A framework that redirects
/hookto/hook/can turn a working endpoint into a 404 or a lost body. - Whether the URL is public. A local or staging host that isn't reachable from the internet fails this way.
- Whether a proxy, load balancer, or path-based router in front of your app is sending that path somewhere else.
A 5xx means your handler threw. Log the raw body before parsing so you can see what it actually received.
A timeout means you didn't respond fast enough. Return 2xx first, process afterwards.
Nothing at all / connection refused means your edge blocked it. Didit delivers from the static IP 18.203.201.92 with a DiditWebhook/2.0 user agent. If you're behind Cloudflare or a WAF with a default-deny posture, allow that IP for the receiving hostname.
#The classic: automatic delivery 404s but Resend works
This one comes up often enough to name. The automated delivery fails with a 404, then clicking Resend on the same event succeeds.
That combination means the payload and your endpoint are both fine - so the difference is timing or path, not content. Check:
- A deploy or restart at the moment of the original delivery. Resend succeeds later because the app is up again.
- A cold start that exceeded your platform's timeout - common on serverless with a slow first invocation.
- Path-based routing that changed between the two attempts, or a rule that only matches some requests.
- Rate limiting or bot protection at your edge that let the manual resend through because it arrived alone rather than in a burst.
The Deliveries tab has both attempts with their timestamps - compare them against your own deploy and error logs for that minute.
#Signature verification failing
Almost always one of three things:
- You hashed re-serialised JSON. HMAC the raw request body bytes exactly as received. Parsing and re-stringifying changes whitespace and key order, and the signature won't match. Most frameworks need explicit configuration to give you the raw body.
- Wrong secret. The signing secret is per endpoint, and it is not your API key. Two endpoints have two different secrets.
- Wrong encoding assumption. If you're unsure whether the secret is used as a literal string or decoded first, don't guess - follow the signature verification reference exactly, and log the raw body while debugging so you can compare.
Never "fix" a signature mismatch by skipping verification. An unverified webhook endpoint will accept a forged approval from anyone who finds the URL, which turns a debugging shortcut into an account-takeover path.
#Two retries is not a queue
When your endpoint answers with a 5xx or a 404, Didit retries twice - roughly 1 minute and then 4 minutes later - and then stops. A delivery that times out (no answer within 5 seconds) or can't connect is not retried automatically. If your endpoint was down for longer than that, those events won't arrive on their own: resend them from the endpoint's Deliveries list, and your team gets a console alert when a delivery fails for good.
Build a reconciliation path: on start-up, poll the decision endpoint for any session you don't hold a terminal status for. Treat webhooks as the fast path and polling as the backstop.
#Testing without running verifications
Send test event on the Webhooks page sends a fully-formed, signed event of whichever kind you pick - approved, declined, in review, KYB, entity, transaction. Use it to prove your endpoint, signature check, and handler work before a real session depends on them.
Sandbox is the other half of this: a sandbox session emits real webhooks with "environment": "sandbox", so you can exercise the whole path end to end for free. See testing in sandbox.
Still stuck? Talk to a human.
Include your organization ID and the session ID you are asking about.
