Debug Webhooks with Delivery Logs (Message Log Workflow)
Debug Webhook Deliveries with Message Log
If you are asking how to debug missing webhook deliveries using delivery logs, start with a strict sequence: ingestion, delivery run, then endpoint response details. That path isolates failures quickly and avoids guessing.
This guide gives you a practical Message Log workflow for Laravel/PHP integrations. You will confirm event ingestion, inspect delivery outcomes, and trace why requests fail or appear to succeed without processing.
Assumption: your webhook endpoint already performs signature verification with raw-body HMAC and constant-time hash_equals comparison.
The debugging order of operations (fastest to slowest)
Confirm the event was ingested (API 201)
First confirm ingestion returned 201. If ingestion failed, there is no delivery run to debug yet. Use How to send a test event and confirm delivery runs.
Confirm a webhook delivery run exists
Open Message Log and find the run tied to your test event. Confirm attempt timestamps and retry count before checking app logs.
Confirm your endpoint response (2xx vs non-2xx)
Use delivery attempts to verify what your endpoint returned on each try. Align expected behavior with What counts as success (2xx) and how retries work.
A) Create a delivery run (SendPromptly ingestion)
Use the Getting Started curl to ingest an event (replace token).
| |
Expected response: 201 (event accepted) and a webhook run appears in Message Log.
Open Message Log now: trigger one event in Sample Project, then trace the webhook run from “created” to “delivered”.
“It never arrived” checklist
Wrong endpoint URL / DNS / TLS
Confirm the exact webhook URL, DNS resolution, and TLS certificate chain. Redirects (301/302) and invalid certs often look like delivery failures.
Signature verification rejecting (401)
If delivery reached your app but gets 401, re-check timestamp tolerance, exact signed string format, and raw-body handling. Verify with hash_equals and never compare signatures with ==.
Network / firewall blocks
Check inbound firewall rules, WAF behavior, reverse proxy allowlists, and container/network policies between edge and app.
Common gotcha: Teams rotate endpoint domains but forget to update webhook destinations. Message Log shows repeated failures, while application logs stay empty because traffic never reaches the service.
“Message Log shows 2xx but nothing happened”
When delivery logs show 200 but my app did not process webhook, the acknowledgment path and processing path are out of sync.
You returned 2xx before persisting (lost payload)
Persist webhook payloads before returning success:
| |
Async job failed silently (no DLQ)
If workers fail after 2xx, you need durable inbox + dead-letter recovery. Implement Build a DLQ so “2xx but nothing happened” can’t happen.
Dedupe bug (ignored event)
Overly broad dedupe logic can drop legitimate events as duplicates. Ensure dedupe keys map to true event identity.
Mini incident: A team acknowledged webhooks and queued jobs, but dedupe used only
event_key(not payload identity). New orders with the same event type were dropped; switching to a stable content-based dedupe key fixed it.
Interpreting common status patterns
Timeouts
If your webhook delivery log shows timeout what to check: worker saturation, DB lock waits, slow downstream APIs, and long synchronous code paths before response.
429 rate limiting
To debug 429 rate limit responses in webhook delivery logs, inspect rate-limit policy scope (IP, token, route), then smooth traffic via queues and bounded concurrency.
5xx dependency failures
5xx usually indicates your app or a dependency failed while processing. Correlate delivery timestamps with dependency health and error spikes.
4xx contract failures (bad request)
Persistent 4xx often means schema drift, auth/signature mismatch, or missing required fields. Use Common API errors and meanings (429, 401, 409) for fast triage.
| Pattern in Message Log | Typical cause | First action |
|---|---|---|
| Repeated timeout | Slow request path or blocked dependency | Return 2xx faster and queue heavy work |
Burst of 429 | Receiver throttling too aggressively | Add buffering/backoff and tune limits |
Repeated 5xx | App or dependency instability | Inspect app errors and dependency health |
Repeated 401/400 | Signature or payload contract issue | Validate raw-body signature and schema |
Common failure modes
- Endpoint returns 301/302 (redirect) -> treated as non-2xx by many senders; retries.
- TLS/cert errors (self-signed, expired) -> connection failures; retries.
- Timeouts (slow code path) -> multiple attempts; duplicates.
- 429 rate limits -> retries amplify; need queue smoothing.
- Signature verification rejects due to raw-body mismatch or clock skew.
- “2xx but nothing happened” because you ack before persisting + job fails later.
- Idempotency bug discards real events as duplicates.
Minimum observability you should add
Correlation IDs (dedupe key)
| |
Log attempt, latency, signature result
Store attempt number, delivery latency, and signature pass/fail result so Message Log entries can be correlated to app behavior in seconds.
Safe payload logging
Only log non-sensitive fields needed for debugging. Redact secrets, tokens, and personal data by default.
Micro checklist:
- Include
dedupe_keyin every webhook log line. - Record
attempt,status_code,latency_ms, andsignature_valid. - Keep enough payload context to debug contract issues, but redact sensitive fields.
- Alert when timeout/429/401 patterns spike across attempts.
Delivery log debugging summary
- Debug in order: ingestion
201-> Message Log run -> endpoint response details. - Most “never arrived” reports are URL/TLS/routing or signature validation issues.
- Most “2xx but nothing happened” incidents come from ack-before-persist, silent async failures, or weak dedupe rules.
- Status code patterns (timeouts, 429, 5xx, 4xx) point to distinct fixes; treat them differently.
- Correlation IDs and safe structured logs make webhook failures reproducible and fast to resolve.
Make debugging deterministic: Store every webhook in an inbox table + DLQ, then verify in Message Log that every attempt ends in 2xx.