Forward Deployed Engineering Tutorial · Integration Engineering · Lesson 10 of 24

Webhooks

Receive events from customer systems safely: signature verification, replay protection, idempotency and fast acknowledgements.

Create a free account to save this page, take notes and track your progress. Sign in

How webhooks work

The customer's system (or a SaaS like Stripe, GitHub or Salesforce) sends an HTTP POST to your endpoint when something happens. You must:

  1. Verify the signature (usually an HMAC of the body with a shared secret) using a constant-time comparison.
  2. Reject old timestamps to prevent replays.
  3. Acknowledge quickly (2xx within seconds) and process asynchronously via a queue.
  4. Deduplicate by event ID: senders retry, so the same event can arrive more than once.
Verify, check freshness, deduplicateTry it yourself
import hashlib, hmac, json

SECRET = b"whsec_demo"
processed = set()

def sign(body, ts):
    return hmac.new(SECRET, f"{ts}.{body}".encode(), hashlib.sha256).hexdigest()

def receive(body, ts, signature, now):
    if not hmac.compare_digest(sign(body, ts), signature):
        return "401 bad signature"
    if now - ts > 300:
        return "400 too old (possible replay)"
    event = json.loads(body)
    if event["id"] in processed:
        return "200 duplicate ignored"
    processed.add(event["id"])
    return f"200 queued {event['type']}"

body = json.dumps({"id": "evt_1", "type": "invoice.paid"})
good = sign(body, 1000)
print(receive(body, 1000, good, now=1010))
print(receive(body, 1000, good, now=1020))          # sender retried
print(receive(body, 1000, "forged", now=1010))
print(receive(body, 1000, good, now=2000))

Output

200 queued invoice.paid
200 duplicate ignored
401 bad signature
400 too old (possible replay)

Ordering

Events can arrive out of order. Use version numbers or timestamps on the resource, or fetch the current state from the API when an event arrives, instead of trusting event order.

Test yourself

Why must webhook handlers deduplicate by event ID?

  1. Senders retry, so the same event can arrive more than once
  2. Event IDs are secret
  3. It speeds up TLS
  4. JSON requires it
Show answer

Answer: Senders retry, so the same event can arrive more than once

At-least-once delivery is the norm for webhooks.

Was this page helpful?