October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk7 min

Building a Webhook Receiver That Is Slow and Flaky on Purpose

A small receiver can delay responses, return error codes, rate-limit, or fail then recover, so you can watch how a sender reacts. Timeouts and retry schedules belong to each provider, so verify them there.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A webhook receiver that deliberately delays its response, returns chosen error codes, rate-limits traffic, or fails a set number of times before succeeding takes only a short script to build. What it lets you observe is how a sender reacts to those conditions. It cannot tell you a universal timeout or retry rule, because those belong to each provider, and you have to read the behavior from that provider’s documentation or its delivery logs.

What the receiver controls and what the sender controls

Before you simulate anything, separate the two sides of a webhook exchange. Your receiver decides how long it waits and what it returns. The sending provider decides how long it waits for you, which status codes count as failures, and how often it tries again.

As an Amazon Associate I earn from qualifying purchases.

Behavior Who sets it Where to verify it
Response delay Your receiver Your code and logs
Status code returned (200, 500, 503, 429) Your receiver Your code and logs
Retry-After header You send it; whether the sender honors it is provider behavior The provider’s documentation and attempt history
Timeout threshold Sender Provider settings or documentation. Twilio’s test tool compares result timing with configured connection and read timeouts.
Retry count and schedule Sender Provider documentation. Stripe says it retries several times but does not publish a fixed count in the cited troubleshooting article.
Which status codes trigger a retry Sender Provider documentation

The practical consequence: a timeout you see in a sender’s log is a fact about that sender’s settings, not about your code. Tune your receiver’s delay against the provider’s timeout, not against a number you assumed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The five failure shapes worth simulating

Each failure shape tests a different part of the sender’s behavior. Simulate them one at a time, so a result can be traced to a single cause.

#1 Best Overall
CanaKit Raspberry Pi 5 Starter Kit PRO - Turbine Black (128GB Edition) (8GB RAM)
  • Includes Raspberry Pi 5 with 2.4Ghz 64-bit quad-core CPU (8GB RAM)
  • Includes 128GB Micro SD Card pre-loaded with 64-bit Raspberry Pi OS, USB MicroSD Card Reader
  • CanaKit Turbine Black Case for the Raspberry Pi 5
  • CanaKit Low Noise Bearing System Fan
  • Mega Heat Sink - Black Anodized

Delayed response

Hold the response for a fixed number of milliseconds before replying with a success code. This tests whether the sender gives up before your receiver answers. If the delay is longer than the sender’s timeout, the sender records a failure even though your handler eventually returned 200.

Server error (500)

Return a 500 for every request, or for a set count of requests. This shows whether the sender retries a generic server failure and how quickly its retries arrive.

Rank #2
CanaKit Raspberry Pi 4 4GB Starter PRO Kit - 4GB RAM
  • Includes Raspberry Pi 4 4GB Model B with 1.5GHz 64-bit quad-core CPU (4GB RAM)
  • Includes Pre-Loaded 32GB EVO+ Micro SD Card (Class 10), USB MicroSD Card Reader
  • CanaKit Premium High-Gloss Raspberry Pi 4 Case with Integrated Fan Mount, CanaKit Low Noise Bearing System Fan
  • CanaKit 3.5A USB-C Raspberry Pi 4 Power Supply (US Plug) with Noise Filter, Set of Heat Sinks, Display Cable - 6 foot (Supports up to 4K60p)
  • CanaKit USB-C PiSwitch (On/Off Power Switch for Raspberry Pi 4)

Temporary unavailability (503 with Retry-After)

Return 503 with a Retry-After header in seconds. This models a receiver that is overloaded or in maintenance. Whether the sender waits the indicated time, retries sooner, or ignores the header is a provider-specific behavior that you must check in its logs or documentation.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Rate limiting (429 with Retry-After)

Count requests in the current window and return 429 once a limit is passed, with a Retry-After header. Test whether the sender backs off after a 429 and whether it keeps every event or drops some. Provider documentation should describe this; do not assume it.

Transient failure followed by recovery

Fail the first N requests, then return 200. This is the most useful shape for checking retries end to end. It confirms that a delivery that eventually succeeds is marked as successful on the sender’s side, and it shows how many attempts were needed.

A minimal receiver you can reconfigure without editing code

The script below uses Node.js’s built-in http module, so it needs no packages. Behavior is set with environment variables, so you can change the failure shape by restarting the process. The code does not verify webhook signatures; a real receiver must do that using the method in each provider’s documentation.

Rank #4
Raspberry SC15184 Pi 4 Model B 2019 Quad Core 64 Bit WiFi Bluetooth (2GB)
  • Broadcom BCM2711, quad-core Cortex-A72 (ARM v8) 64-bit SoC @ 1. 5GHz
  • 2. 4 GHz and 5. 0 GHz IEEE 802. 11b/g/n/ac wireless LAN, Bluetooth 5. 0, BLE
  • 2 × USB 3. 0 ports, 2 x USB 2. 0 Ports
  • 2 × micro HDMI ports supproting up to 4Kp60 video resolution
  • Micro SD card slot for loading operating system and data storage
const http = require('http');

const PORT = Number(process.env.PORT || 8080);
const DELAY_MS = Number(process.env.DELAY_MS || 0);
const FAIL_FIRST = Number(process.env.FAIL_FIRST || 0);
const FAIL_STATUS = Number(process.env.FAIL_STATUS || 503);
const RETRY_AFTER = Number(process.env.RETRY_AFTER || 0);

let attempts = 0;

const server = http.createServer((req, res) => {
  let size = 0;
  req.on('data', (chunk) => { size += chunk.length; });
  req.on('end', () => {
    attempts += 1;
    const n = attempts;
    console.log(new Date().toISOString(), req.method, req.url, 'request', n, 'bytes', size);
    setTimeout(() => {
      const headers = { 'Content-Type': 'application/json' };
      if (n <= FAIL_FIRST) {
        if (RETRY_AFTER > 0) headers['Retry-After'] = String(RETRY_AFTER);
        res.writeHead(FAIL_STATUS, headers);
        res.end(JSON.stringify({ error: 'simulated failure', request: n }));
        return;
      }
      res.writeHead(200, headers);
      res.end(JSON.stringify({ ok: true, request: n }));
    }, DELAY_MS);
  });
});

server.listen(PORT, () => console.log('listening on', PORT));

To produce a receiver that waits three seconds, rejects the first two requests with 503 and a five-second Retry-After, then succeeds:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
DELAY_MS=3000 FAIL_FIRST=2 FAIL_STATUS=503 RETRY_AFTER=5 node flaky-receiver.js

Two details to keep in mind. The request counter is global, so every sender that hits the endpoint shares it, and restarting the process resets it. The Retry-After value is sent in seconds; HTTP also allows an HTTP-date form, which this script does not produce.

Best Value
CanaKit Raspberry Pi 5 16GB Starter Kit PRO - Turbine Black (128GB Edition) (16GB RAM)
  • Includes Raspberry Pi 5 16GB with 2.4Ghz 64-bit quad-core CPU (16GB RAM)
  • Includes 128GB Micro SD Card pre-loaded with 64-bit Raspberry Pi OS, USB MicroSD Card Reader
  • CanaKit Turbine Black Case for the Raspberry Pi 5
  • CanaKit Low Noise Bearing System Fan
  • Mega Heat Sink - Black Anodized

Making a provider reach a local receiver

Most providers cannot call localhost. You need a public HTTPS address that forwards to your local port. Twilio’s guide to testing webhooks describes a local setup that uses a public tunnel, and it states plainly: “To create these tunnels, use ngrok.” (Twilio, Test webhook delivery, local testing section.)

  1. Start the receiver on a local port, for example PORT=8080 node flaky-receiver.js.
  2. Start a tunnel that forwards a public HTTPS address to localhost:8080. Twilio’s guide names ngrok for this.
  3. Copy the HTTPS address the tunnel prints and enter it as the endpoint in the provider’s test or webhook configuration.
  4. Trigger one test event and check the receiver’s console output before you trust the result.

Some tools require HTTPS even when the endpoint is on your own machine. PayPal’s simulator posts mock events to a listener on HTTPS port 443, so a plain HTTP localhost address will not work there.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Comparing provider test tools

Test tools differ in where the request comes from, which failures they can produce, and what they let you inspect. The table covers the tools discussed in this article; it does not rank them.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Tool Request source Failure responses you can simulate What you can observe Endpoint location
PayPal Webhooks simulator (PayPal Developer) Queued mock events for demonstration and listener validation Not stated; the page describes failed connection or delivery, not controlled error codes Event status is updated with error details when a connection or delivery fails. PayPal says a queued event often arrives within a minute; this is an operational estimate, not a guarantee. HTTPS listener on port 443
Twilio Test webhook delivery (Twilio) One webhook sent to a chosen URL using a named Webhook Setting Not stated; the feature is marked Public Beta and may change Result timing, compared with configured connection and read timeout values Public URL, including a local receiver behind a public tunnel
Jitterflow Webhook Failure Simulator (Jitterflow) A hosted bin URL that receives your sender’s requests 200; 500; 503 with Retry-After; 429 with Retry-After; failure that recovers to 200 The bin’s log. This is the vendor’s own description of the tool, not an independent evaluation. Hosted by the vendor
Custom receiver (the script above) Whatever sender you point at it Any delay, status code, Retry-After value, or failure count you code Your own console and logs, including the exact body size and request number you choose to record Wherever you run it, behind a tunnel if local

Diagnosing from the provider’s side

Your receiver’s log shows only what arrived. To see what the sender recorded, use the provider’s delivery view. Stripe’s troubleshooting article says Stripe retries several times when an event cannot be delivered, and it recommends inspecting the endpoint’s failed events and each attempt’s status, including the HTTP status and response details (Stripe Support). Compare the attempt list with your receiver’s log. If Stripe shows three attempts and your log shows two, a request either never reached the receiver or was rejected before your handler ran.

Keep test data synthetic

A hosted endpoint is only as private as its URL. Jitterflow warns that anyone who has the bin URL can read its log, and it says to send only test data. The same rule applies to any receiver you expose through a tunnel:

Quick Recap

Bestseller No. 1
CanaKit Raspberry Pi 5 Starter Kit PRO - Turbine Black (128GB Edition) (8GB RAM)
CanaKit Raspberry Pi 5 Starter Kit PRO - Turbine Black (128GB Edition) (8GB RAM)
Includes Raspberry Pi 5 with 2.4Ghz 64-bit quad-core CPU (8GB RAM); CanaKit Turbine Black Case for the Raspberry Pi 5
$259.95
Bestseller No. 2
CanaKit Raspberry Pi 4 4GB Starter PRO Kit - 4GB RAM
CanaKit Raspberry Pi 4 4GB Starter PRO Kit - 4GB RAM
Includes Raspberry Pi 4 4GB Model B with 1.5GHz 64-bit quad-core CPU (4GB RAM); Includes Pre-Loaded 32GB EVO+ Micro SD Card (Class 10), USB MicroSD Card Reader
$159.99
Bestseller No. 4
Raspberry SC15184 Pi 4 Model B 2019 Quad Core 64 Bit WiFi Bluetooth (2GB)
Raspberry SC15184 Pi 4 Model B 2019 Quad Core 64 Bit WiFi Bluetooth (2GB)
Broadcom BCM2711, quad-core Cortex-A72 (ARM v8) 64-bit SoC @ 1. 5GHz; 2. 4 GHz and 5. 0 GHz IEEE 802. 11b/g/n/ac wireless LAN, Bluetooth 5. 0, BLE
$92.97
Bestseller No. 5
CanaKit Raspberry Pi 5 16GB Starter Kit PRO - Turbine Black (128GB Edition) (16GB RAM)
CanaKit Raspberry Pi 5 16GB Starter Kit PRO - Turbine Black (128GB Edition) (16GB RAM)
Includes Raspberry Pi 5 16GB with 2.4Ghz 64-bit quad-core CPU (16GB RAM); CanaKit Turbine Black Case for the Raspberry Pi 5
$419.99
  • Use generated payloads with fake identifiers and amounts.
  • Do not send live customer data, personal information, or production secrets in headers.
  • Stop the tunnel when you finish, so the public address no longer forwards to your machine.

Troubleshooting

  • The sender logs a timeout, but your receiver logged a 200. Your delay is probably longer than the sender’s timeout. Compare DELAY_MS with the timeout configured for that sender; Twilio’s test result shows the timing against its connection and read timeouts.
  • Nothing reaches the receiver. Confirm the tunnel is still running and that the endpoint in the provider’s configuration matches the current public address. If your tunnel issues a new address each time it starts, update the endpoint each session.
  • The sender retries more often than you expected. The retry schedule is set by the provider. Read its documentation or attempt history rather than inferring the schedule from your own server.
  • The sender ignores Retry-After. Whether a sender honors that header is provider-specific. Check the attempt timestamps in the provider’s log to see the actual gap between attempts.
  • The failure never stops. The request counter is global and only resets when the process restarts. Restart the receiver to begin a new run.
  • A PayPal simulated event does not appear. Check the event’s status in the simulator. PayPal updates it with error details when a connection or delivery fails.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.