Reliable webhook integrations need more than an endpoint that returns a successful status. This guide explains how to verify incoming requests, acknowledge deliveries safely, prevent duplicate work, process events through a queue, recover failures, and monitor the integration over time.
Overview
A webhook is an HTTP request sent by one system when an event occurs in another system. Payment updates, repository changes, subscription events, form submissions, and deployment notifications are common examples. The receiving application is responsible for deciding whether the request is authentic, whether it has already been processed, and how to complete the work without losing the event or keeping the sender waiting.
A dependable design separates receipt from processing. The public webhook endpoint should perform a small set of time-sensitive tasks:
- Accept the request over HTTPS.
- Capture the raw body and relevant headers.
- Verify the provider's signature and timestamp rules.
- Record an event identifier and reject known duplicates.
- Place the validated event on a durable queue or processing table.
- Return the status code expected by the provider.
Business logic can then run asynchronously. This reduces timeout risk and makes retries, replay, and dead-letter handling possible. The exact signature algorithm, retry schedule, event fields, and delivery limits vary by provider, so treat the provider's current documentation as the source of truth rather than copying assumptions between integrations.
For the surrounding cloud architecture, pair this approach with a deliberate staging process and observable runtime. The cloud app staging environment checklist can help structure safe integration tests, while the cloud app observability guide provides a broader framework for logs, metrics, and traces.
What to track
1. Request verification
Verify the request before trusting its contents. Most providers use a shared secret and a signature derived from some combination of the raw request body, a timestamp, and a version marker. Do not parse and reserialize JSON before verification unless the provider explicitly instructs you to do so; even harmless formatting changes can alter the signed bytes.
A provider-agnostic verification flow looks like this:
rawBody = readRequestBytes(request)
signatureHeader = request.headers["provider-signature"]
if missing(rawBody, signatureHeader):
return HTTP_400
expected = hmacSHA256(WEBHOOK_SECRET, signedMessage(rawBody, signatureHeader))
if not constantTimeEqual(expected, extractSignature(signatureHeader)):
recordSecurityEvent("invalid_webhook_signature")
return HTTP_401
if timestampIsOutsideAllowedWindow(signatureHeader):
return HTTP_401
acceptForProcessing(rawBody)
return HTTP_202
Use constant-time comparison for signatures, keep secrets outside source control, and rotate them according to your operational policy. Avoid logging secrets, full authorization headers, or sensitive payload fields. If the provider supports multiple signing secrets during rotation, accept the old and new secret only for the intended transition period.
2. Event identity and idempotency
Retries and duplicate deliveries are normal possibilities. Create a durable uniqueness rule around the provider's event ID when one is available. A useful event record may include:
- provider and event_id, together forming a unique key;
- event type and schema version;
- received time, delivery time, and processing status;
- payload reference or encrypted payload;
- attempt count, last error, and next retry time;
- correlation or request ID for tracing.
If the provider does not supply a stable event ID, derive an idempotency key from documented immutable fields, or generate an internal receipt ID while using a business-level uniqueness constraint. Do not rely on an in-memory cache as the only duplicate protection. A process restart or multiple application instances can otherwise allow the same event to run twice.
3. Processing and side effects
Make the consumer idempotent as well as the receiver. For example, before issuing a refund, creating a shipment, or provisioning an account, check whether that business action has already been recorded. Where supported, pass your own idempotency key to downstream APIs. Database transactions can help link event state to application state, but be explicit about what is and is not covered by the transaction.
A queue-based flow is usually easier to operate than doing all work inside the HTTP request:
- The endpoint verifies and stores the event.
- The endpoint enqueues a reference to the stored event.
- A worker claims the event with a lease or processing state.
- The worker performs idempotent business operations.
- The worker marks the event complete, retryable, or permanently failed.
4. Delivery and failure signals
Track more than a generic error count. At minimum, measure accepted requests, invalid signatures, duplicate events, queue depth, processing latency, retry count, permanent failures, and events in the dead-letter queue. Keep structured logs for event ID, provider, event type, status transition, attempt number, and a redacted error reason.
Cadence and checkpoints
Webhook reliability is an operational practice, not a one-time setup task. Use a monthly or quarterly review cadence, with an additional review after every incident or provider change.
During implementation
- Confirm the provider's current signature construction, timestamp tolerance, and required response codes.
- Test malformed JSON, missing headers, invalid signatures, stale timestamps, duplicate deliveries, and unknown event types.
- Simulate a slow worker and confirm that the receiver still acknowledges valid deliveries within the provider's expected window.
- Verify that secrets, payloads, and personally identifiable information are not exposed in logs.
- Test a controlled replay from a stored event without creating a duplicate business side effect.
Monthly or quarterly review
- Compare successful processing with retry and dead-letter volumes.
- Inspect the oldest queued event and the longest processing times.
- Review invalid signature attempts and unexpected event types.
- Confirm that alert routes, dashboards, runbooks, and on-call ownership still work.
- Check that retention, encryption, access controls, and payload redaction remain appropriate.
- Run a replay drill using a non-production or carefully isolated event.
Record the provider documentation version or review date in the integration's runbook. This creates a clear checkpoint when an SDK, API version, signing requirement, or delivery behavior changes.
How to interpret changes
A rise in retries does not always mean the provider is unreliable. First separate the failure modes. A queue backlog with normal receipt rates suggests worker capacity, database latency, or a downstream dependency problem. A drop in received events may indicate a provider configuration, routing, DNS, certificate, or authentication issue. A sudden increase in invalid signatures can point to secret rotation mistakes, clock skew, a changed signing format, or hostile traffic.
Look at trends by provider, event type, deployment version, and response class. A broad increase after a release deserves a different response from a problem limited to one event type. Measure both delivery success and business completion: returning a successful HTTP response proves that the event was accepted, not that the intended action finished correctly.
When an event fails, classify it before retrying:
- Transient: timeouts, temporary database unavailability, or rate limiting. Retry with exponential backoff and jitter.
- Permanent input error: unsupported schema, missing required field, or invalid business state. Send to a dead-letter queue for review instead of retrying indefinitely.
- Security failure: invalid signature or unacceptable timestamp. Reject and record a security event; do not enqueue the payload.
- Operationally unknown: an unclassified failure. Preserve the event and context, cap automatic retries, and require investigation.
Backoff intervals should be bounded and provider-aware. Add jitter so many failed events do not retry simultaneously. Respect provider rate limits and delivery limits, and avoid an arrangement where both your system and the provider retry aggressively at the same time.
Replay and dead-letter procedure
A replay tool should require authentication, show the event ID and prior attempts, and support a dry-run or isolated mode where practical. Before replaying, identify the original failure and confirm that the consumer's idempotency checks are active. Preserve the original event rather than editing it silently; if a transformation is necessary, store the transformed version and explain the change in the audit record.
Dead-letter queues need ownership and an expiry policy. An unreviewed queue is not a recovery strategy. Each item should be assigned a disposition such as replayed, discarded with reason, corrected and replayed, or escalated to the provider.
When to revisit
Use this checklist whenever you add a provider, change an event subscription, upgrade an SDK, rotate a secret, migrate an app deployment platform, or investigate a missed event. Revisit the full integration at least monthly or quarterly according to its risk and volume, and perform an immediate review after an incident.
Before declaring a webhook integration ready, confirm that:
- The endpoint captures the raw body and verifies signatures before parsing trusted data.
- Secrets are managed securely and rotation has been tested.
- Event IDs or equivalent idempotency keys are protected by a durable uniqueness rule.
- Receipt is separated from asynchronous business processing.
- Retries use bounded exponential backoff with jitter and respect provider guidance.
- Events, errors, queue depth, latency, and dead-letter items are observable.
- Replay is controlled, auditable, and safe against duplicate side effects.
- A runbook identifies owners, alert thresholds, retention rules, and escalation paths.
Keep this checklist beside the integration code and update it when the provider's signature requirements, retry behavior, delivery limits, SDK examples, or security guidance changes. For practical cloud deployment context, see how to deploy a web app to the cloud; for related developer utilities, the developer tools online toolkit covers JSON, SQL, JWT, and cron tasks that often support integration troubleshooting.