If you've ever built a Stripe integration, you know the pain: every webhook event hits your endpoint — even the ones you don't care about. charge.updated flooding your logs while you're only watching for payment_intent.succeeded. Sensitive card metadata sitting in plain text in your inspection logs. Fields named one thing in Stripe but something different in your database schema.
Normally, solving this means writing middleware. A filter here, a field mask there, a rename transformer somewhere else. It's boilerplate — but it has to be written, tested, and maintained.
I built a small feature into HookTest (a webhook testing and inspection tool) called Transform Rules that handles all three cases without writing any code.
What Transform Rules Do
When a webhook hits your HookTest endpoint, before the request is stored, you can apply a chain of rules in order:
- Filter — only capture requests where a JSON field matches a condition (equals, contains, starts with). Anything that doesn't match is acknowledged with 200 OK but silently dropped.
-
Mask — replace a sensitive field value with a placeholder (
***by default). The payload is stored with the masked value — the original never touches disk. -
Rename — rename a JSON key before storage (e.g.,
user_id→userId).
Rules run in the order you define them. Filter rules always run first (even if you add them later), so a dropped request never gets transformed.
A Real Example: Filtering Stripe Events
Say you're testing a checkout flow and you only want to see checkout.session.completed events — nothing else.
- Create a HookTest endpoint and copy the URL into your Stripe webhook dashboard.
- Open the "Transform Rules" tab on your endpoint.
- Add a Filter rule: path =
type, operator =eq, value =checkout.session.completed.
Now when Stripe fires payment_intent.created, charge.updated, or any other event, HookTest acknowledges it silently. Your inspection log stays clean.
// Only this gets stored:
{
"type": "checkout.session.completed",
"data": { "object": { "id": "cs_test_..." } }
}
// Everything else → acknowledged but dropped
Masking Sensitive Data
Stripe payloads can contain customer emails, card last-four digits, billing addresses. If you're sharing your HookTest endpoint URL with a teammate to debug something, you probably don't want that data in the captured log.
Add a Mask rule: path = data.object.customer_details.email, mask = [REDACTED].
The stored payload looks like:
{
"data": {
"object": {
"customer_details": {
"email": "[REDACTED]",
"name": "Jane Smith"
}
}
}
}
The raw email never gets written to storage.
Why Not Just Write a Filter in Your App?
You can — but there's a catch. When you're testing, you want to inspect what Stripe is actually sending before your app logic runs. If your filter middleware drops the event, you've lost visibility into the raw payload.
HookTest sits upstream of your app. It captures first, applies your rules, then you can see exactly what would have been stored — and replay it to your real endpoint when you're ready.
The replay feature is especially useful here: capture once, inspect, then fire it at http://localhost:3000/webhooks as many times as you need.
Availability
Transform Rules are a Pro feature ($9/mo). Free tier gets unlimited basic capture and inspection with no rules.
If you're testing Stripe, GitHub, Twilio, or any other webhook source, give HookTest a try — the free tier should cover most debugging sessions.
HookTest is built on Cloudflare Workers + D1 with a zero-dependency dashboard. The transform rule engine is ~120 lines of TypeScript.
Top comments (0)