What is a webhook and how is it different from a REST API?
In a REST API, you ask: your application makes a GET request and waits for the response. In a webhook, the system tells you: when something happens (payment approved, commit pushed, user registered), the server fires a POST to the URL you registered.
This inversion of control makes webhooks very efficient — no polling, no open connections — but also harder to test and debug.
Stripe / GitHub
Publisher
Publisher
POST →
httpdrop
Inspector / Relay
Inspector / Relay
→
Your API
Consumer
Consumer
The 5 pillars of a robust webhook
-
1
Asynchronous processing — Return 200 immediately and process the payload in the background (queue, worker). Never do slow operations before responding.
-
2
Idempotency — The same event can arrive more than once (automatic retry). Use the
eventIdfrom the payload to deduplicate: if already processed, return 200 without reprocessing. -
3
HMAC signature verification — Validate the
X-Signatureheader (HMAC-SHA256 of body + secret) before processing any payload. -
4
Retry with exponential backoff — If your API returns 5xx, the publisher should retry: 1min → 5min → 30min → 2h. 4xx errors should not trigger retries.
-
5
Logs and alerts — Record each received event with: eventId, processing status, latency and retry count. Alert when the failure rate exceeds 5%.
Testing webhooks with httpdrop
httpdrop works as a temporary receiver endpoint to inspect real webhook payloads — and also as a sender for outbound webhooks from your test application.
Inspect real payloads — Use the httpdrop URL as the webhook URL in Stripe/GitHub during development and see the exact payload that will reach your API.
Test outbound webhooks — Configure httpdrop as the destination for your application's webhooks and inspect whether the payload, headers and HMAC signature are correct.
Replay — Resend any captured webhook with one click to test again without having to retrigger the original event.
HMAC signature verification (Node.js)
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Security: Never expose your webhook URL in public logs or API responses. Treat it as a credential. Always use HTTPS and validate the signature before processing any data.