GUIDE / api
How to Test Webhooks: Events, Retries, Signatures, and QA
How to test webhooks with event payloads, signatures, retries, idempotency, ordering, security, local tunnels, mocks, and QA examples.
How to test webhooks is a practical QA problem because webhooks are asynchronous, public-facing, and easy to misunderstand. A normal API test calls an endpoint and immediately checks a response. A webhook flow starts somewhere else, sends an event to your system later, may retry, may arrive out of order, and may be delivered more than once. That changes the testing strategy.
This guide explains webhook testing for QA engineers and SDETs. You will learn how webhooks work, what to test, how to test locally, how to validate payloads and signatures, how to test retries and idempotency, how to automate webhook checks, and which mistakes cause production incidents.
How to Test Webhooks: The Short Workflow
To test webhooks, trigger a real or simulated event, capture the HTTP request sent to the webhook endpoint, validate signature and headers, verify payload schema and business fields, return the expected status, confirm processing side effects, test duplicate delivery, test retries, test out-of-order events, and verify security behavior for invalid or replayed requests.
This workflow is broader than checking "did the callback arrive." A webhook can arrive with the wrong event type, invalid signature, missing field, duplicate ID, old timestamp, or unexpected order. The endpoint can return 200 but fail processing later. The provider can retry and create duplicate state. QA needs to test the whole lifecycle.
For general API fundamentals, read API testing tutorial. Webhooks build on those concepts but add asynchronous delivery and public endpoint risk.
What Is a Webhook?
A webhook is an HTTP callback sent by one system to another when an event happens. For example:
- Payment provider sends
payment.succeeded. - Shipping provider sends
shipment.delivered. - Git platform sends
pull_request.opened. - CRM sends
contact.updated. - Learning platform sends
course.completed.
The receiving system exposes a URL, often called a webhook endpoint. The provider sends an HTTP request to that URL with headers and a payload. The receiver verifies the request and processes the event.
Example webhook payload:
{
"id": "evt_123",
"type": "payment.succeeded",
"created": "2026-07-10T09:15:00Z",
"data": {
"paymentId": "pay_456",
"amount": 4999,
"currency": "USD",
"customerId": "cus_789"
}
}
A webhook endpoint might respond with HTTP 200 to acknowledge receipt. If it fails, the provider may retry later. That retry behavior is both helpful and risky.
Webhook Testing Mindset
Webhook testing is about events, not only endpoints. The question is not just "does this URL return 200." The question is "does the system respond correctly when this event is delivered under realistic and hostile conditions?"
Test at three layers:
| Layer | What to Test |
|---|---|
| Delivery | Provider sends event to correct URL with expected headers |
| Acceptance | Receiver validates signature, schema, timestamp, and event type |
| Processing | Business state changes exactly once and failures are handled |
This separation helps debugging. If a test fails, you can identify whether the event was not sent, was rejected, or was accepted but not processed.
Webhook flows are often eventually consistent. The endpoint may accept the event quickly, then queue background processing. Tests should not assume the final state is available instantly. Use polling with a timeout.
Local Webhook Testing
Local testing is useful before deploying to shared environments. The challenge is that external providers need a public URL. A local tunnel solves this by forwarding a public HTTPS URL to your local machine.
Typical workflow:
- Start your app locally.
- Start a tunnel to the local port.
- Configure the provider webhook URL to the tunnel URL.
- Trigger a provider event.
- Inspect incoming request headers and body.
- Check app logs and database state.
- Return expected status codes.
Example:
Local app: http://localhost:3000/webhooks/payments
Tunnel URL: https://example-tunnel.test/webhooks/payments
Provider config: send payment events to tunnel URL
Local tunnels are excellent for exploration, but they are not enough for release confidence. Also test in staging with production-like URLs, TLS, auth, queues, and environment configuration.
What to Verify in Webhook Headers
Headers often carry security and routing information.
Common headers:
- Event signature.
- Timestamp.
- Event ID.
- Delivery ID.
- User agent.
- Content type.
- Idempotency key.
- Provider version.
Test that required headers are present and validated. If a signature header is missing, the endpoint should reject the request. If timestamp is too old, the endpoint may reject it to prevent replay attacks. If content type is wrong, the endpoint should handle or reject it according to the contract.
Do not log sensitive signatures or secrets in plain text. Logs should help debugging without leaking credentials.
Payload Validation
Payload testing includes structure and business meaning.
Validate:
- Event ID exists and is unique.
- Event type is supported.
- Created timestamp is valid.
- Required
datafields exist. - Amounts, currency, IDs, and statuses match expectations.
- Unknown fields do not break processing.
- Missing required fields are rejected or dead-lettered.
- Unsupported event types are ignored safely or rejected as designed.
Use schema validation where possible, but do not stop at schema. A schema may prove amount is a number, but not that the amount matches the order. Add business checks.
Example test cases:
| Case | Input | Expected Result |
|---|---|---|
| Valid payment succeeded | Known order ID and paid amount | Order marked paid once |
| Missing payment ID | Remove paymentId | Event rejected or sent to error queue |
| Unknown event type | payment.unknown | Ignored safely with audit log |
| Amount mismatch | Event amount differs from order | Payment held for review |
| Duplicate event ID | Same event delivered twice | One business update only |
Signature Verification Testing
Webhook endpoints are public URLs. Signature verification proves the event came from the expected provider and was not modified.
Test:
- Valid signature accepted.
- Missing signature rejected.
- Invalid signature rejected.
- Signature generated with wrong secret rejected.
- Old timestamp rejected if replay protection is required.
- Modified body with original signature rejected.
Body parsing can break signature verification. Some providers require verifying the raw request body before JSON parsing changes whitespace or encoding. QA should confirm implementation details with developers because a webhook can look correct in application logs while signature verification uses a different body representation.
Security tests should also verify that rejected webhook requests do not trigger processing. A 400 response is not enough if the system still updates state.
For related auth and token concepts, see API authentication testing.
Retry Testing
Webhook providers usually retry when the receiver fails. The retry policy may depend on status code, timeout, or provider rules.
Test:
- Receiver returns 500.
- Receiver times out.
- Receiver returns 429.
- Receiver returns 400 for invalid request.
- Provider retries only retryable failures.
- Provider stops retrying after success.
- Duplicate retry does not duplicate business state.
Example retry test:
Given the webhook endpoint is configured to fail once
When provider sends payment.succeeded
Then the first delivery receives 500
And provider retries according to policy
And the second delivery receives 200
And the order is marked paid exactly once
If the provider cannot be forced to retry in a test environment, simulate retries by sending the same valid event multiple times. That still tests idempotency.
Idempotency Testing
Idempotency means processing the same event multiple times has the same business result as processing it once.
Test duplicates:
- Same event ID sent twice.
- Same delivery ID sent twice.
- Same business object sent through different event IDs.
- Retry arrives after processing completed.
- Retry arrives while first processing is still in progress.
Expected behavior depends on the system, but common patterns include storing processed event IDs, using database constraints, and making business state transitions safe.
Example:
| Duplicate Condition | Expected Result |
|---|---|
Same evt_123 delivered twice | Second delivery acknowledged but skipped |
Two payment.succeeded events for same payment | Order remains paid once |
| Retry during processing | Lock or idempotency guard prevents duplicate action |
| Different event ID but same refund ID | Refund created once |
Idempotency is not optional for serious webhook systems. Providers can and do deliver duplicates.
Ordering and Race Conditions
Webhooks can arrive out of order. For example, invoice.paid might arrive before customer.created in a delayed system. A shipment.updated event may arrive after shipment.delivered. QA should test ordering assumptions.
Test:
- Later status arrives before earlier status.
- Cancellation arrives after success.
- Duplicate old event arrives after new state.
- Related events arrive close together.
- Background workers process events in parallel.
The system should either handle out-of-order events or define clear rules for rejecting and reconciling them. Event timestamps and version numbers can help. Tests should verify state does not move backward incorrectly.
Automating Webhook Tests
Webhook automation can be done with provider test tools, local mock senders, WireMock, API clients, and application-level test hooks.
Approaches:
- Use provider dashboard to send test events.
- Use provider API to trigger events.
- Use a local script to POST signed payloads.
- Use WireMock to verify outbound webhooks.
- Use contract examples as fixtures.
- Poll application state after event acceptance.
Example automation flow:
create unpaid order through API
generate signed payment.succeeded payload
POST payload to webhook endpoint
expect HTTP 200
poll order API until status is PAID
send same payload again
verify order still has one payment record
For mocks and outbound webhook verification, read API mocking with WireMock.
Webhook Observability Checks
Webhook testing should include observability. When a webhook fails in production, teams need to answer what was received, whether it was trusted, how it was processed, and why it failed. QA can test whether that evidence exists before release.
Check that the system records:
- Event ID.
- Provider name.
- Event type.
- Delivery ID if available.
- Received timestamp.
- Verification result.
- Processing status.
- Retry count.
- Error reason.
- Correlation ID.
Avoid storing raw sensitive payloads unless the product and security teams approve it. A safer pattern is to store metadata, sanitized payload excerpts, and links to secure logs.
Also test dashboards or admin views if support teams use them. A webhook can be technically processed while support has no way to investigate a failed event. That creates operational risk.
Webhook Test Environment Design
Webhook environments need careful configuration. A staging provider account should send events to staging, not to a developer tunnel or production endpoint. Secrets should be different per environment. Event IDs and test data should be clearly marked.
Use this environment checklist:
| Item | QA Question |
|---|---|
| Endpoint URL | Does provider configuration point to the correct environment? |
| Secret | Is the signing secret environment-specific and secure? |
| Events | Are only needed event types enabled? |
| Test data | Can test events be linked to test users or orders? |
| Retries | Can retry behavior be observed safely? |
| Logs | Are delivery and processing logs available? |
| Isolation | Can tests run without affecting real customers? |
Misconfigured webhook URLs are a common cause of integration failures. Include configuration verification in the test plan, not only payload verification.
Webhooks and Business Reconciliation
Webhook systems should have a reconciliation path because events can be missed, delayed, or rejected. QA should ask what happens if the system does not receive a critical event. Many payment, shipping, and subscription systems include a scheduled job that queries the provider and repairs local state.
Test reconciliation when possible:
- Block or skip one webhook event.
- Confirm local state remains pending or marked for review.
- Run reconciliation job or trigger.
- Verify the system fetches provider state.
- Confirm local state updates correctly.
- Verify duplicate actions are not created if the webhook arrives later.
This is advanced testing, but it protects real business workflows. A webhook endpoint can be correct and still not be enough if delivery is not guaranteed.
Common Mistakes in Webhook Testing
The first mistake is testing only a valid event. Real webhook risk appears in retries, duplicates, invalid signatures, missing fields, and out-of-order delivery.
The second mistake is assuming HTTP 200 means success. The endpoint may acknowledge the event and fail processing later. Always verify downstream state.
The third mistake is skipping signature tests. A public callback without proper verification can become a security issue.
The fourth mistake is not testing duplicate delivery. Duplicate events are normal in webhook systems.
The fifth mistake is using fixed sleeps for async processing. Use polling with a clear timeout and show the last observed state when failing.
The sixth mistake is logging secrets. Signature headers, raw payloads with personal data, and provider secrets need careful redaction.
The seventh mistake is ignoring unsupported event types. Providers may add events. Your system should handle unknown types safely.
The eighth mistake is treating provider test events as complete coverage. Provider test events may not represent every real production payload variation.
The ninth mistake is ignoring event versioning. Providers may change payload shape or send version headers. QA should test the current version and know how older versions are handled.
The tenth mistake is not testing disabled or unsubscribed events. If the provider lets teams choose event types, verify the application behaves correctly when a required event type is not configured.
Webhook Test Case Template
Use a consistent template for webhook cases so reviewers can see the whole event lifecycle.
| Field | What to Capture |
|---|---|
| Event Type | Provider event name, such as payment.succeeded |
| Trigger | How the event is created or simulated |
| Headers | Signature, timestamp, event ID, delivery ID |
| Payload | Fixture file, schema, and important business fields |
| Expected Response | Status returned to the provider |
| Processing Result | Database, API, queue, email, or state change |
| Duplicate Behavior | Expected result when same event is sent again |
| Retry Behavior | Expected result after timeout or 500 |
| Security Result | Behavior for invalid signature or replay |
| Observability | Logs, audit record, correlation ID |
This template prevents a narrow test that checks only the HTTP response. It also helps teams discuss ownership. Developers may own signature verification, QA may own scenario coverage, DevOps may own endpoint configuration, and support may own operational visibility.
Example Webhook Scenario Set
For a payment webhook, a practical regression set might include:
- Valid payment succeeded marks order paid.
- Duplicate payment succeeded does not create a second payment record.
- Invalid signature is rejected and does not update the order.
- Payment failed marks order payment status failed and keeps order open.
- Refund succeeded updates refund status and audit history.
- Unknown event type is ignored safely.
- Timeout causes provider retry and final processing succeeds.
- Out-of-order failed event after succeeded event does not move the order backward.
That set is stronger than testing one provider dashboard sample. It covers trust, state, duplicates, retries, and ordering.
Where QABattle Fits in Your Practice
Webhook testing is a strong way to practice event-driven QA. It forces you to think beyond request and response into delivery, trust, retry, and state. You can practice that thinking in the QABattle testing battles: identify the event, the receiver, the security check, the side effect, the duplicate behavior, and the failure policy.
How to test webhooks comes down to proving the event lifecycle. Capture the delivery, validate the request, process safely, handle retries, prevent duplicates, and protect the endpoint. When those checks are in place, webhook integrations become much less mysterious and much safer to release.
One final practice is to keep webhook fixtures under review. Store representative valid, invalid, duplicate, and legacy payloads in version control with safe synthetic data. When the provider announces a new event version or field change, update those fixtures before release testing starts. This keeps webhook QA grounded in concrete examples rather than memory, dashboard screenshots, or one-off manual events.
FAQ
Questions testers ask
What is webhook testing?
Webhook testing verifies that event notifications are sent, received, authenticated, processed, retried, and stored correctly. It checks payload structure, signature verification, event types, idempotency, ordering, failure handling, retry policy, and security around public callback endpoints.
How do I test webhooks locally?
Test webhooks locally by running your application on a local port and exposing it through a secure tunnel such as ngrok or a similar tool. Configure the provider to send events to the tunnel URL, then inspect requests, responses, logs, signatures, and processing results.
What should a webhook endpoint return?
A webhook endpoint should usually return a fast 2xx response after validating and accepting the event for processing. If verification fails, it should return an appropriate 4xx response. Long business processing should often happen asynchronously to avoid provider timeouts.
How do you test webhook retries?
Force the receiving endpoint to return 500, timeout, or temporarily reject a valid event, then verify the provider retries according to the documented schedule. Also verify duplicate handling so repeated deliveries do not create duplicate orders, refunds, emails, or state changes.
Why is idempotency important in webhook testing?
Webhook providers may deliver the same event more than once. Idempotency ensures processing the same event repeatedly produces only one business effect. Without idempotency checks, duplicate webhooks can create duplicate payments, notifications, shipments, or records.
RELATED GUIDES
Continue the route
API Testing Tutorial: A Beginner's Complete Guide
API testing tutorial for beginners: learn REST API checks, CRUD test cases, tools, status codes, and a practical checklist for reliable API quality.
API Mocking with WireMock: Stubs, Tests, and Examples
API mocking with WireMock guide for QA teams covering stubs, request matching, delays, faults, recording, contracts, CI, and mistakes.
API Automation Framework: Build a Maintainable Test Suite
API automation framework guide covering architecture, tools, test data, assertions, CI, reporting, mocks, contracts, and mistakes to avoid.
API Authentication Testing: OAuth, JWT, and API Keys
Learn API authentication testing for OAuth 2.0, JWT expiry and refresh flows, API keys vs bearer tokens, and broken authentication test cases.