Loading...

Skip to content
MemonaIQ
ToolsLearnCompany
Sign in
ToolsLearnCompanyBrowse all tools
Knowledge Center
In this topic
APIs
What is an API? Definition, examples and how it worksREST API design principles: URLs, methods and errorsAPI authentication: API keys vs OAuth 2.0 vs JWTAPI rate limiting: algorithms, 429 and Retry-AfterWebhooks explained: signatures, retries and idempotencyOpenAPI breaking changes: what breaks API clientsRequest vs response schema compatibility in APIsHow enum changes break API clients and generated SDKsOpenAPI 3.0 vs 3.1: differences and migration checklistMCP stdio vs Streamable HTTP: transport security compared
A-Z glossary
HomeKnowledgeAPIs
APIs•4 min read•intermediate

Webhooks explained: signatures, retries and idempotency

How webhooks deliver events over HTTP, how to verify HMAC signatures and block replays, and how to handle retries, duplicates and the choice versus polling.

How a webhook works

A webhook is an HTTP request that a provider sends to a URL you register whenever a chosen event happens, such as a payment succeeding. Your endpoint receives a JSON description of the event and answers with a 2xx status to confirm it arrived.

Setup happens once. You register an HTTPS endpoint with the provider, choose the event types you want, and store the signing secret the provider gives you. From then on, the provider pushes events to you instead of waiting for you to ask.

A delivery usually carries an event ID, an event type, a timestamp and the affected object. Header names vary by provider.

HTTP Snippet
http
POST /webhooks/payments HTTP/1.1
Host: shop.example.com
Content-Type: application/json
X-Webhook-Timestamp: 1767222000
X-Webhook-Signature: 5f2b9c...

{
  "id": "evt_7Hq2Lk",
  "type": "payment.succeeded",
  "created": 1767222000,
  "data": { "payment_id": "pay_4410", "amount": 2500, "currency": "usd" }
}

Verify the signature before trusting the payload

A webhook endpoint is a public URL, so anyone can send it a request. Providers sign each delivery so you can tell theirs apart. They compute an HMAC (a keyed hash such as HMAC-SHA256) over the body, often with a timestamp, using a secret only you and they know.

The header names and the exact signed string differ between providers, so follow each provider's documentation. Keep the secret in server-side configuration and rotate it if it leaks.

Your endpoint recomputes the HMAC and compares it with the signature header. Three details decide whether that check holds:

  • Compute the HMAC over the raw body bytes exactly as received. Parsing the JSON and serializing it again can change whitespace or key order, and the signature will no longer match.
  • Compare signatures in constant time, with a function such as crypto.timingSafeEqual in Node.js or hmac.compare_digest in Python. An ordinary string comparison can leak, through its timing, how many leading characters matched.
  • If the signature is missing or doesn't match, reject the delivery with a 4xx status and don't process it.
JAVASCRIPT Snippet
javascript
import crypto from 'node:crypto';

// rawBody is a Buffer holding the body exactly as received, before JSON parsing.
function isValidSignature(rawBody, timestamp, signatureHex, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.')
    .update(rawBody)
    .digest();
  const received = Buffer.from(signatureHex, 'hex');
  return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

Reject old deliveries to stop replays

A valid signature proves the provider produced the content. It doesn't prove the request is fresh: someone who captures a delivery can send the same bytes again later, and the signature still matches.

When the provider includes a timestamp in the signed content, reject deliveries whose timestamp is more than a few minutes from your server's clock. Keep server clocks synchronized with NTP so honest deliveries pass.

A tolerance window still allows a replay inside it, so also record the IDs of events you have processed and ignore repeats. The same record handles the provider's own retries.

Respond quickly, then process asynchronously

Providers wait only a short time for your response, and they treat a timeout or a non-2xx status as a failed delivery. Slow work inside the request handler, such as sending email or calling other APIs, turns into timeouts and repeated deliveries.

Keep the handler short. Verify the signature, check the timestamp, store the event or put it on a queue, and return 200 or 204. A background worker then does the processing.

Return an error status only when you want the provider to try again, for example when you couldn't store the event.

Expect retries, duplicates and events out of order

Providers retry failed deliveries, typically with growing delays over hours or days. That makes delivery at-least-once: the same event can arrive twice, for example when your response was lost after you had processed the event.

Make the handler idempotent by using the event ID as an idempotency key. A unique constraint on the stored ID turns a duplicate into a no-op instead of a second shipment or a second email.

Arrival order isn't reliable either: a refund event can arrive before the payment event it refers to. Compare timestamps or fetch the current state from the API before acting, and reconcile periodically in case an outage outlasted the retry schedule.

Webhooks or polling

Polling means calling the API on a schedule to ask whether anything changed. It is simple and needs no public endpoint, but it spends requests when nothing changed, counts against rate limits, and delays updates by up to one interval.

Webhooks deliver changes within moments and send nothing when nothing happens. In return you run a public HTTPS endpoint and handle signatures, retries and outages. Many integrations combine the two: a webhook triggers the work, an API call fetches current state, and an occasional poll catches anything missed.

For streaming updates to a browser or app, WebSockets and server-sent events keep one connection open instead of sending separate requests.

Previous guide
API rate limiting: algorithms, 429 and Retry-After
Next guide
OpenAPI breaking changes: what breaks API clients

References

  • RFC 2104: HMAC: Keyed-Hashing for Message Authentication

On this page

How a webhook worksVerify the signature before trusting the payloadReject old deliveries to stop replaysRespond quickly, then process asynchronouslyExpect retries, duplicates and events out of orderWebhooks or polling
Updated:2026-10-25
Format:Technical Guide
MemonaIQ

Focused tools for everyday digital work.

  • 18 focused tools
  • 9 run in your browser
Browse all tools Get help

Featured tools

URL Intelligence PlatformImage CompressorInvoice StudioDNS LookupAI Token Counter

Explore

All toolsKnowledge CenterGuidesResourcesCompany

Support

HelpContactSecurity

Legal

PrivacyTermsCookies

© 2026 MemonaIQ. Made in Colombo.

Back to top

MemonaIQ