Skip to main content

API Testing

Testing Webhooks: The API Testing Problem Most Frameworks Ignore

Jovan RadivojevicSep 29, 20263 min read
Testing Webhooks: The API Testing Problem Most Frameworks Ignore

I've reviewed API suites that call themselves fully covered and still get blindsided by a webhook bug in week one of going live. Not because anyone skipped testing on purpose. It's that a webhook isn't a request-response pair, it's a promise your server will call someone else back later, and testing a promise needs a different setup than testing a response you get immediately.

Why the usual assertion doesn't tell you much

A 200 OK from your webhook receiver confirms the request was accepted for processing. It doesn't confirm the payload was handled correctly, the callback fired, or that anything downstream of "we got the event" actually worked. Most webhook tests I see stop right there, at the acknowledgment, because that's the part that's easy to assert on.

What actually needs testing

Signature verification. If your endpoint accepts an unsigned or tampered payload without rejecting it, that's not a webhook bug, it's an open door. Test the rejection path explicitly, not just the happy path where the signature happens to be valid.

Idempotency under redelivery. Every major provider redelivers events at least once by design, usually after a timeout or a non-2xx response. If your handler processes the same event twice and does something twice, that's where duplicate-charge and duplicate-email bugs come from. The fix is an idempotency key check before you do the side-effecting work, and it needs a test that fires the same payload twice on purpose.

Timeout behavior on your end. If your handler is slow, does the sender retry the way its docs say it will, and does your system handle that retry correctly, or does slowness on your side turn into duplicate processing because nobody tested what happens when your own response takes longer than expected?

Test against real payload shapes, not the one you imagined

A hand-written mock payload reflects what you think the provider sends. It drifts from what the provider actually sends the moment their API adds a field or changes a nested structure, and your test keeps passing against a shape that no longer exists in production. Where the provider offers a webhook replay or event-log export, use captured real payloads in your test fixtures instead of a JSON blob someone wrote from memory six months ago.

The bug I've traced back to this more than once

A handler that works in every manual test, because a person testing it by hand only ever sends the event once. It breaks the first time the real provider redelivers, which every provider eventually does, and by then it's a customer-facing incident instead of a test failure. If your webhook suite doesn't include a deliberate duplicate-delivery test, that gap is exactly where this bug is waiting.

This sits right alongside the discipline in writing resilient API tests and contract testing: the contract has to cover the callback, not just the call. It's one of the first things we check in an API testing engagement.