The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Build a webhook API as a small, authenticated HTTPS endpoint that accepts a provider’s POST, verifies the signature against the exact raw request bytes, rejects stale or malformed events, records a unique delivery ID, places valid work on a durable queue, and returns a 2XX response quickly. The HTTP handler should acknowledge receipt; a worker should perform billing, email, database, or other slow operations. This design handles forged requests, retries, duplicate deliveries, provider timeouts, and temporary outages without losing events.
What a webhook API does
A webhook is an HTTP callback. A provider sends an event to a URL that your application controls instead of making your application poll for changes. Your endpoint might be POST /webhooks/orders, with a JSON body describing an order payment, shipment, account change, or another event.
The endpoint is an integration boundary, not a normal browser form. Treat every request as untrusted until its signature, timestamp, event type, schema, and tenant identity have passed validation. A reliable implementation follows this order:
- Accept only the intended HTTPS POST route.
- Capture the unmodified request bytes.
- Read the provider’s signature and delivery headers.
- Compute the provider-specified HMAC and compare it in constant time.
- Reject invalid or stale requests before parsing or acting on the payload.
- Parse JSON and validate its version, type, required fields, and account scope.
- Insert the delivery ID under a database uniqueness constraint.
- Queue business work and return a documented 2XX response.
Design the endpoint contract first
Use a narrow route and HTTPS
Create separate routes when providers or event domains have different secrets and contracts, such as /webhooks/stripe and /webhooks/github. Require HTTPS in production; GitHub’s webhook guidance specifically says the server should use an HTTPS connection. Do not put a secret in a URL query string, commit it to source control, or print it in logs. Store it in a secrets manager or protected environment variable.
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minute#1 Best Overall
Subscribe only to events you handle
Enable the smallest event set that your application actually processes. Fewer subscriptions reduce attack surface, traffic, queue load, and the chance that an unimplemented event is acknowledged as if it were understood.
Define an envelope and version
Document fields such as event_id, event_type, schema_version, occurred_at, tenant_id, and data. Keep the provider’s delivery ID even if the payload has its own business object ID. Delivery IDs identify transport attempts; business IDs identify domain objects and are not interchangeable.
Verify signatures correctly
Preserve the raw body
HMAC verification is calculated over exact bytes. JSON middleware can change whitespace, key order, escaping, or character encoding before your code sees the body, causing a valid signature to fail. Configure the webhook route to receive a raw byte buffer and parse JSON only after authentication succeeds. GitHub documents X-Hub-Signature-256 as an HMAC-SHA-256 digest of the request body and recommends it over the compatibility SHA-1 header.
Use the provider’s exact signing recipe
Providers differ in header names and signed data. One may sign only the body; another may sign a timestamp, a dot, and the body. Follow that provider’s specification exactly, including the required prefix such as sha256=, UTF-8 handling, timestamp tolerance, and secret encoding. Never silently substitute your own format.
Free tools Windows power users keep installed
One-click scans. No signup required.
Compare in constant time
Use a constant-time comparison after confirming equal lengths. A normal string comparison can reveal information through timing differences. Rotate secrets by accepting the old and new secret during a documented overlap, then remove the old one.
Complete Node.js and Express example
This example keeps raw bytes for the route, validates an HMAC-SHA-256 header, deduplicates the delivery ID, and queues work. The insertDeliveryOnce function must be backed by durable storage with a unique constraint; an in-memory set is not sufficient.
import express from 'express';
import crypto from 'node:crypto';
const app = express();
const secret = process.env.WEBHOOK_SECRET;
if (!secret) throw new Error('WEBHOOK_SECRET is required');
// Do not put express.json() before this route.
app.post('/webhooks/orders', express.raw({ type: 'application/json', limit: '1mb' }), async (req, res) => {
const supplied = req.get('X-Signature-256') || '';
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(req.body).digest('hex');
const valid = supplied.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(supplied), Buffer.from(expected));
if (!valid) return res.sendStatus(401);
const deliveryId = req.get('X-Delivery-Id');
if (!deliveryId) return res.status(400).send('Missing delivery ID');
let event;
try {
event = JSON.parse(req.body.toString('utf8'));
} catch {
return res.status(400).send('Invalid JSON');
}
if (event.schema_version !== 1 || typeof event.type !== 'string') {
return res.status(400).send('Unsupported event');
}
// Enforce UNIQUE(delivery_id) in a durable database.
const firstSeen = await insertDeliveryOnce(deliveryId, event);
if (firstSeen) {
await queue.publish({ eventId: deliveryId, type: event.type, payload: event });
}
return res.sendStatus(202);
});
app.listen(process.env.PORT || 3000);
Header names in this sample are illustrative. Replace them with the sender’s contract. GitHub uses X-GitHub-Event, X-GitHub-Delivery, and X-Hub-Signature-256; a different provider may use different names and a timestamped signature base.
Make retries harmless with idempotency
Providers retry when a connection fails, a timeout occurs, or a non-2XX status is returned. The same event can therefore arrive more than once, and two deliveries can race. Make the delivery ID a unique database key and perform insertion atomically:
Recommended Free Tools
- Begin a short database transaction.
- Insert the delivery ID, event type, tenant, received time, and verification result.
- If the unique constraint reports an existing ID, commit and return 2XX without publishing another job.
- For a new ID, insert an outbox record or publish to a durable queue in a way that cannot lose the event between database commit and queue submission.
Idempotency must also exist in the worker. Use a business-operation key such as order_id + operation when sending email, charging a card, or mutating an external system. A transport delivery ID prevents duplicate handling of one delivery; it does not automatically prevent two distinct events from repeating a business action.
Acknowledge fast and process asynchronously
Return 202 Accepted after authentication, validation, durable recording, and queue handoff. Do not wait for email, billing calls, image processing, or a long database report. GitHub’s current best-practice guidance sets a 2XX response target within 10 seconds and recommends a queue when processing may exceed that window.
Choose a queue with durable storage, visibility timeouts, retry limits, and a dead-letter queue. Workers should record start time, finish time, attempt count, and the final error. Keep the HTTP handler’s work bounded so provider retries do not create a feedback loop during an outage.
Test a webhook locally and in production
Send a signed local request with cURL
Generate the same signature your server expects, then send the raw JSON unchanged. This shell example uses OpenSSL and the sample header format:
body='{"schema_version":1,"type":"order.paid","order_id":"ord_123"}'
signature=$(printf '%s' "$body" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" -hex | sed 's/^.* //')
curl -i https://your-domain.example/webhooks/orders
-H 'Content-Type: application/json'
-H "X-Signature-256: sha256=$signature"
-H 'X-Delivery-Id: delivery_123'
--data-binary "$body"
Use --data-binary, not a tool that reformats the body. In development, expose the endpoint through a TLS tunnel or a local HTTPS server and restrict test secrets to non-production data.
Exercise the failure paths
- Change one body byte after computing the signature; expect
401. - Send malformed JSON with a valid signature; expect
400. - Send the same delivery ID twice; both requests may receive 2XX, but only one job should be published.
- Delay the worker while keeping the handler fast; verify the provider does not retry.
- Make the queue unavailable; verify the handler returns a non-2XX or uses a transactional outbox rather than acknowledging lost work.
- Replay an old timestamped request; expect rejection according to the provider’s allowed clock skew.
Validate payloads and tenant scope
After signature verification, enforce a schema with a library or explicit checks. Validate required fields, maximum lengths, enum values, numeric ranges, and the declared schema version. Reject unknown tenant or account identifiers rather than trusting a user-controlled field to select a database.
For multi-tenant systems, map the provider account in a server-side table tied to the endpoint secret. Log the mapped internal tenant, not merely the untrusted payload value. If the provider supports account-scoped endpoints, configure the narrowest scope that meets your integration needs; Stripe, for example, documents endpoint configuration with an enabled-event list and account or Connect scope.
Observability, replay, and recovery
For every delivery, log a correlation record containing delivery ID, event type, tenant or account, signature outcome, received timestamp, enqueue result, response status, latency, worker attempts, and final processing status. Redact secrets, authorization headers, payment data, and unnecessary personal information.
Retain the raw payload only according to your privacy and retention requirements, encrypt it at rest, and restrict replay permissions. Provide an operator workflow to requeue a failed delivery after correcting the cause. Keep a dead-letter queue and a reconciliation job that compares important local state with the provider API; reconciliation is valuable when an outage exceeds the provider’s retry window or a delivery was permanently lost.
Compare webhook providers before integrating
Evaluate the provider contract rather than assuming all webhooks behave alike:
Rank #3
| Area | Questions to answer |
|---|---|
| Authentication | Which signature algorithm, header, secret format, timestamp tolerance, and raw-body rules apply? |
| Delivery identity | Is there a stable delivery ID, and is it unique across accounts and event types? |
| Retries | What causes a retry, how many attempts occur, and can operators redeliver a specific event? |
| Acknowledgement | Which status codes count as success, and what timeout does the sender enforce? |
| Ordering | Are events ordered per resource, per account, or not at all? |
| Scope | Can endpoints be limited to selected events, tenants, or connected accounts? |
| Replay and history | Is there a dashboard or API for inspecting payloads and replaying failures? |
GitHub exposes event and delivery headers and its SHA-256 signature header. Stripe documents a configured URL, enabled-event list, and account or Connect endpoint scope. Read each provider’s current documentation before implementing the adapter.
Performance and cost considerations
Keep the synchronous path small: TLS termination, body-size enforcement, signature verification, schema checks, one idempotency write, and queue submission. Cap payload size, reject unsupported content types, and apply rate limits appropriate to the provider’s documented burst behavior. Measure p50, p95, and maximum acknowledgement latency separately from worker latency.
Queue capacity, database uniqueness checks, log retention, and replay storage are the main operating costs. A durable outbox adds a database table and publisher process but protects against the failure window between acknowledging HTTP and publishing a job. For high-volume integrations, partition queues by tenant or event family so one noisy account cannot delay every customer.
Troubleshooting common failures
Every valid request returns 401
Confirm that raw bytes reach the verifier, the secret is the right environment’s value, the signature prefix and case match the provider, and the signed string includes the required timestamp. Check for a reverse proxy that decompresses, transcodes, or rewrites the body.
Requests succeed but duplicate charges or emails occur
Verify a durable unique constraint on delivery ID and an idempotency key in the worker. Ensure the insert and queue handoff cannot race, and inspect whether the provider sent two different delivery IDs for the same business event.
The provider reports timeouts
Move all external calls and heavy queries behind the queue. Check DNS, TLS negotiation, load-balancer idle timeouts, cold starts, and database connection acquisition. Return a 2XX only after the event is durably recorded; otherwise allow a retry.
Events disappear during an outage
Inspect the provider’s delivery history and redeliver missed events after recovery. Add a transactional outbox, monitor queue depth, and run reconciliation against the provider API for critical objects.
JSON parsing fails intermittently
Do not parse before verification, and do not assume every event is JSON. Enforce the provider’s content type, character encoding, compression rules, and schema version. Preserve the original bytes for diagnosis subject to retention policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If you need a clean screenshot of a webhook dashboard, delivery log, or status page for documentation or review, ScreenshotNeo provides a single HTTP call. Its API accepts the consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing result. Its MCP server lets Claude, Cursor, or another MCP client call take_screenshot, get_page_info, and capture_pdf.
curl -G 'https://api.screenshotneo.com/v1/shot' -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/webhook-dashboard -o shot.webp
See the ScreenshotNeo API documentation for the other 63 options, including full-page capture, CSS selectors, custom headers and cookies, device presets, PDF output, waits, blocking rules, signed links, asynchronous jobs, and bulk capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Rank #4
FAQ
Should a webhook endpoint return 200 or 202?
Either is valid when documented by the provider. Use 202 when you accepted the event for asynchronous processing; use 200 when your contract treats the request as fully handled. The important conditions are durable acceptance and a response within the provider’s timeout.
Can I verify a signature after parsing JSON?
Not safely in general. Verification must use the exact raw request bytes. Parse only after the HMAC check unless the provider explicitly defines a canonicalized representation.
How long should webhook data be retained?
Choose retention based on replay, audit, privacy, and regulatory needs. Store enough metadata to diagnose and deduplicate deliveries, encrypt sensitive payloads, and enforce deletion policies.
What if the provider does not offer signed webhooks?
Use HTTPS, an authentication mechanism supported by the provider, strict source and schema validation, rate limiting, and a narrowly scoped endpoint. Treat IP allowlists as an additional control, not a replacement for cryptographic authentication when signing is available.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Frequently Asked Questions
Should a webhook endpoint return 200 or 202?
Either can be correct when the provider accepts it. Return after durable acceptance, and document whether the status means completed work or queued work.
Can I verify a signature after parsing JSON?
Usually no. Verify the exact raw bytes first, then parse the authenticated payload.
How long should webhook data be retained?
Retain the metadata and payload only as long as replay, audit, privacy, and regulatory requirements justify, with encryption and deletion controls.
What if the provider does not sign webhooks?
Use HTTPS, provider-supported authentication, strict validation, rate limits, and narrowly scoped routes; IP filtering is supplementary.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteQuick Recap
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.




