Debug Stripe Webhooks
Time: ~10 minutes | Difficulty: Beginner | Prerequisites: Stripe account with webhook access, Slashbin account
TL;DR: Put Slashbin in front of your Stripe endpoint. Every event is stored with its raw payload, transform result, delivery attempts, and response codes — so when something "doesn't work," you debug from evidence instead of re-firing test events.
The Problem
A Stripe event fired. Your system shows nothing happened. Now what?
Without stored evidence, you can't tell where it went wrong:
- Was it never sent? Maybe Stripe didn't dispatch it, or your endpoint URL was misconfigured.
- Was it rejected? A signature mismatch, an invalid payload, a timeout at the edge.
- Was it transformed wrong? Your parser exploded on a field shape you weren't expecting.
- Was it delivered and dropped? Your handler returned 200 but the business logic silently no-op'd.
Raw endpoints keep no history. Your only diagnostic is re-triggering a test event and hoping the failure reproduces — while the original event, the one that actually broke, is gone.
The Solution
Route Stripe through a Slashbin project. Every event is stored end-to-end:
- The raw payload exactly as Stripe sent it — bytes and headers.
- The transform result — what your Golden Model produced (or the error it threw).
- Every delivery attempt to your endpoint — with response code, response body, and timing.
- The final disposition — delivered, retrying, or in the DLQ.
When something goes wrong, you open the delivery log, find the event, and read the story. No re-firing. No guessing.
Outcome: You debug Stripe webhooks by reading stored evidence, not by trying to reproduce failures in a test environment that doesn't match production.
Step 1: Route Stripe Through a Slashbin Project
If you haven't already put Slashbin in front of Stripe, follow Retry Stripe Webhooks Reliably — the setup is the same. Debugging is a byproduct of the gateway pattern.
Once the project exists:
- Stripe posts to a Slashbin Ingestion URL (Dashboard → Developers → Webhooks).
- Slashbin verifies the Stripe signature at the gateway and stores the raw payload.
- Every downstream stage — transform, delivery attempts, retries — is recorded against that event.
Nothing about your endpoint changes; you gain a delivery log for free.
Step 2: Open the Delivery Log for the Failing Event
In the Slashbin Console, open the project and go to the Delivery Log.
Find the event you want to debug — by Stripe event id (evt_...), by timestamp, or by filtering to failed deliveries only.
Each entry shows a single stored delivery record. Its shape looks like this:
{
"event_id": "evt_1MtwBwLkdIwHu7ixV1YrJ8vN",
"received_at": "2026-07-21T14:03:22.104Z",
"source": "stripe",
"event_type": "invoice.paid",
"signature_verified": true,
"transform": {
"status": "ok",
"model_version": 7
},
"attempts": [
{
"attempt": 1,
"at": "2026-07-21T14:03:22.482Z",
"destination": "Billing Service",
"url": "https://api.example.com/webhooks/stripe",
"response_status": 502,
"response_body": "Bad Gateway",
"duration_ms": 4213
},
{
"attempt": 2,
"at": "2026-07-21T14:03:52.501Z",
"destination": "Billing Service",
"url": "https://api.example.com/webhooks/stripe",
"response_status": 500,
"response_body": "TypeError: Cannot read property 'customer' of undefined",
"duration_ms": 118
}
],
"disposition": "in_dlq"
}The record above tells you three things at a glance:
- Signature verification passed — this wasn't a bad-secret problem.
- The transform succeeded — the Golden Model produced valid output.
- Your handler returned a real error —
TypeError: Cannot read property 'customer' of undefined— and after retries, the delivery landed in the DLQ.
That's the fix location: your handler, not Stripe, not the transform.
Step 3: Pinpoint the Failed Stage
The delivery record is stage-ordered on purpose. Walk it top-down:
signature_verified: false→ Slashbin rejected the event at the gateway. Check the signing secret in the project settings against Stripe's current signing secret for that endpoint.transform.status: "error"→ The Golden Model threw. The record includes the error and the offending source field so you can fix the mapping and re-publish the model.attempts[].response_statusin the 4xx range → Your endpoint rejected the payload. The storedresponse_bodyis the exact response your handler returned.attempts[].response_statusin the 5xx range ortimeout→ Your endpoint errored transiently. Slashbin will retry with backoff; if all attempts exhaust, the disposition becomesin_dlq.disposition: "delivered"but the downstream system shows nothing → Delivery succeeded end-to-end. The bug is downstream of your handler's 2xx response — dropped inside your own code, not in the pipeline.
You don't guess between these — you read the record.
Step 4: Replay After the Fix
Once you've deployed the fix, the same event is re-runnable from the gateway:
- Open the Dead Letter Queue.
- Find the event by id or filter.
- Click Replay. Slashbin re-delivers the stored payload to your endpoint.
Stripe is not involved in the replay — the payload comes from Slashbin's storage, not a re-trigger of Stripe's send. That matters when the original event was one-of-a-kind (a specific customer's failed subscription renewal) and can't be reproduced on demand.
If the fix worked, the replay attempt logs a 2xx and the disposition flips to delivered. If it didn't, you get a fresh attempt record to read.
Why Slashbin vs a Raw Handler
- Debug from stored evidence, not re-triggered test events. The exact payload that broke is on disk, along with every attempt, response code, and timing.
- Stage-by-stage attribution. Signature, transform, delivery — each stage's result is captured, so you don't have to guess where it failed.
- Replay from storage, not from Stripe. Fixed the bug? Re-run the exact original event. Stripe never re-sends anything.
- DLQ instead of silent drops. Failed deliveries surface. They don't disappear.
Next Steps
- Guarantee delivery survives a bad deploy — see Retry Stripe Webhooks Reliably.
- Learn how one event can be routed to multiple systems — see Destination Model.
- Walk the full replay flow end-to-end — see Replay & Retry.
- Browse every supported source and destination on Integrations.