Skip to content
← Free FDE training guides

Free learning / 50-minute webhook proof workshop

Webhook verification: signed delivery is not new business work

Verify exact signed bytes, distinguish authentic stale messages from fresh repeats, and bind a business receipt to one local effect through ten offline HMAC and SQLite cases.

No account needed. Use your own copy for writing and keep the original as a reference.

Engineering lab library / Run locally

Choose your next agent lab.

Start with the durable workflow reference, then choose the boundary you want to test. Open its walkthrough for setup, commands and expected observations.

Synthetic records. No provider key or model inference. Runs locally with the standard library.

  1. Start here / Python 3.11+ · Standard library

    Webhook byte verification and business event receiving

    Compare authentic, tampered, stale, repeated and conflicting deliveries against actual local receipt/effect state.

    Python 3.11+ · Standard-library HMAC and SQLite · Ephemeral keys · No network

Original LockedIn Labs workshop · 50 minutes · Python 3.11+ standard library. Ten offline cases verify actual HMAC-SHA256 values and execute scratch SQLite transactions. Each case generates fresh ephemeral keys; neither keys nor MAC values appear in reports. There is no HTTP endpoint, provider account, real event, persistent database or production authentication capability.

1. Give each boundary its own question

A webhook is a source sending a notification to a receiver, commonly through HTTP. Before acting, the receiver needs to know which configured source contract applies, whether the received bytes authenticate under that contract, whether their signed time is permitted, whether the payload is supported, and whether the business event was already applied. A successful connection answers none of those questions.

An HMAC, or keyed-hash message authentication code, binds bytes using a shared secret. Verification demonstrates agreement with the configured key and message bytes; it is not encryption, a public-key signature or proof of which individual held the shared key. A valid MAC does not grant end-user access or make the reported business change appropriate.

The source in this exercise is selected by receiver configuration, not by an unverified request field. It reports a fictional synthetic.delta of four units. The effect is a counter in one disposable memory database, not a payment, account update, clinical action or external command.

Illustrative learning artifact

One event. Two transaction boundaries.

Source transactionCase update + outbox eventCommit both or neither

event-42 → delivery may repeat →

Receiver transactionEvent identity + taskDuplicate delivery → existing result

2. Verify the exact envelope before parsing JSON

This workshop uses an original teaching envelope. Its internal lab-delivery-id, lab-sent-at and lab-mac names are not a provider integration. The sender computes HMAC-SHA256 over:

Text example
delivery_id ASCII bytes + "." + sent_at ASCII bytes + "." + exact body bytes

The raw body contains a separate stable event_id. The delivery ID identifies an attempt in this fixture; a later attempt can have another ID and send time while reporting the same business event. Real providers assign different semantics to their headers and IDs. Use their supported SDK and exact documented scheme; do not substitute these lab fields into a real endpoint.

The receiver bounds the body and header shapes, decodes one version-one Base64 MAC into exactly 32 bytes, and compares it with the full expected digest using hmac.compare_digest. It does not choose the comparison length from a supplied digest. It verifies the signed timestamp policy before parsing JSON or opening a receipt transaction.

A JSON parser can treat differently spaced documents as the same object. That does not make their bytes identical. Verifying a reserialized object instead of the original bytes changes the authenticated message. Keep raw bytes at the real HTTP/framework boundary according to the source contract; this offline program receives bytes directly and does not test middleware behavior.

UNDERSTAND THE SYSTEM

Verify the delivery, then receive the event once

Six authored frames explain ten offline HMAC/SQLite comparisons. The original envelope binds delivery ID, timestamp and exact body bytes. Ephemeral keys and MAC values are omitted from reports; this is not a live endpoint or a provider implementation.

FOLLOW THE WORK

Watch one example, step by step.

6 authored stepsYou control the pace
01 / EXPLORE THE PARTSControls loading · written explanations below
Direction of information or workReturn / feedback

WHAT THIS PART DOES

Received byte envelope

The fixture supplies exact body bytes and separately signed delivery/time fields. Business event identity is inside the body; a new delivery attempt does not necessarily mean a new event.

CONNECTED TO
  • Authenticate bytes: Configured-key verification
Read the full worked example
  1. 01
    1 · Exact received bytes are authenticated

    The valid case checks the configured key, full MAC, signed delivery ID/time and original body. It then parses one supported payload and commits one receipt plus four local counter units. This is an original offline contract, not a deployed provider endpoint.

    ACTUAL VALID COMPARISON: One MAC comparison · one parse · one receipt · four units

  2. 02
    2 · Equal JSON meaning is not equal signed bytes

    Changing units, reserializing spacing, using another key or changing the delivery ID without re-signing fails MAC verification. The reserialized body has the same JSON meaning, but different bytes. These rejected attempts do not reach receiver JSON parsing or receipt state.

    ACTUAL BYTE/KEY MISMATCHES: MAC mismatch · zero receiver parses · zero effects

  3. 03
    3 · A valid MAC can still be too old or too far ahead

    Stale and future carry authentic envelopes but signed times outside the fixture's -30 to 300 second policy. Both are refused before JSON parsing. The clock is fixed fictional input, not measured wall time; real clock and skew policy remain owner responsibilities.

    ACTUAL TIME-POLICY REFUSALS: MAC valid · age 301 / -31 refused

  4. 04
    4 · A freshly signed repeat is still the same event

    Repeat signs DEL-2 at a new time while retaining EVT-1 and the identical raw body. Both deliveries verify and parse. The second finds the source/event receipt and replays it; the counter remains four. MAC bytes, send time and attempt ID are not this contract's business receipt key.

    ACTUAL REPLAY COMPARISON: Two valid deliveries · one receipt · four units

  5. 05
    5 · Authentication does not override a receipt conflict

    Event-conflict re-signs changed units. Same-event-new-bytes re-signs different spacing with unchanged parsed meaning. Both MACs pass, but the exact-byte receipt contract conflicts under EVT-1. The first four-unit state remains. Byte conflict is not a claim that whitespace changed the business meaning.

    ACTUAL STRICT RECEIPT BINDING: Valid new MAC · changed body hash · first state retained

  6. 06
    6 · Change signed age without widening the policy

    Change only the valid case's send age from zero to 301 seconds. The sender creates a valid MAC for those bytes and fields; the unchanged receiver policy refuses its time. No JSON parse or receipt follows. A freshness decision cannot replace event idempotency, and neither establishes business permission.

    CONTROLLED SIGNED-TIME CHANGE: Age 0 applies · age 301 is refused

Read the connections in plain English
  1. 01
    Received byte envelope → Configured-key verification

    Changing a signed field or using another key fails the original envelope.

  2. 02
    Configured-key verification → Receiver time policy

    A cryptographic match does not settle signed time policy.

  3. 03
    Receiver time policy → Supported event payload

    Rejected MAC/time cases do not parse JSON or open a receipt transaction.

  4. 04
    Supported event payload → Receipt and local effect

    Bind the source/event receipt and local counter effect together.

  5. 05
    Receipt and local effect → Receiving evidence

    Compare committed, replayed and conflicting results without adding an external effect claim.

  6. 06
    Configured-key verification → Receiving evidence

    Keep a stage reason without reporting keys or MAC values.

  7. 07
    Receiver time policy → Receiving evidence

    Retain a valid-MAC result separately from the refused timestamp.

Check your understanding: Does a fresh valid MAC mean the receiver should apply a new business effect?

No. The repeat and conflict cases authenticate fresh deliveries while retaining one business event identity. Check the supported payload and source/event receipt contract before another effect; authentication, freshness, idempotency and business authorization answer different questions.

3. Run ten independently reset receiving cases

Download the Python runner into a disposable folder:

Code example
python3 --version
python3 fde-webhook-receiving.py > webhook-original.json
python3 fde-webhook-receiving.py --case valid --age-seconds 301 > webhook-aged.json
python3 fde-webhook-receiving.py --case repeat
python3 fde-webhook-receiving.py --case same-event-new-bytes

On Windows, use py -3 instead of python3. There are no package installations or network requests. Each case creates and closes its own SQLite memory database. Shell redirection overwrites a file of the same name; retain original and changed reports separately. The CLI accepts no secret, URL, arbitrary body or external input file.

Predict the default outcomes:

CaseBoundary actually testedActual local outcome
validOriginal bytes, configured key and permitted send timeOne receipt; four counter units
body-changedUnits changed to seven while retaining the old MACMAC mismatch; no parsing or receipt transaction
body-reserializedSame JSON meaning, changed spacing, old MACMAC mismatch before receiver JSON parsing
wrong-keySender uses another ephemeral keyMAC mismatch; no local effect
changed-delivery-idDelivery ID changed after signingMAC mismatch because the ID is authenticated too
staleAuthentic bytes sent 301 logical seconds earlierTimestamp refused; no receiver JSON parsing
futureAuthentic bytes dated 31 logical seconds aheadTimestamp refused under this fixture's policy
repeatNew delivery ID/time, same event and exact body, new valid MACFirst applied, second replayed; still four units
event-conflictNew valid delivery, same event ID, changed unitsMAC passes, receipt conflicts; first effect retained
same-event-new-bytesNew valid delivery and same JSON meaning, different body bytesMAC passes, strict byte-bound receipt conflicts

Reports retain only synthetic delivery/time/body evidence, hashes, stage results, counters and database before/after state. MAC values and keys are omitted. The actual receiver increments mac_comparisons, json_parses and receipt_transactions when those stages execute; a rejected request does not become a recorded business receipt.

4. Treat freshness and idempotency as different controls

The receiver uses fixed fictional time 1800000000, never the machine's wall clock. Its authored policy admits a signed age from minus 30 through plus 300 seconds. That is a teaching policy, not a universal webhook tolerance. Both stale and future cases have valid MACs and still fail this time check. A real owner must agree clock source, permitted skew, retry behavior and recovery evidence.

Now compare the default valid case with --age-seconds 301. Only that case's signed send time changes. Its MAC remains correct, but the receiver refuses it without parsing the payload or touching receipt state. The age control does not widen the receiver's policy. It helps distinguish a message-authentication result from a freshness decision.

Within the window, a delivery can still repeat. The repeat case independently signs delivery DEL-2 with a new send time while retaining event EVT-1 and its exact body. Both deliveries pass verification and payload checks. The receipt key is configured source plus business event ID, not MAC bytes, timestamp or delivery-attempt ID. An identical event/body replays the existing receipt without another counter increment.

A short timestamp window does not replace durable deduplication; an in-memory receipt does not establish durable recovery. If history expires or the process restarts, a real receiver needs an explicit retention/reconciliation contract. This exercise has no persistence, concurrent delivery or cross-system exactly-once guarantee.

5. Keep the receipt and local effect together

After verification, the receiver rejects duplicate JSON keys, unsupported fields/kinds, invalid event identity, Boolean/string/fractional units and out-of-contract amounts. A valid MAC authenticates supplied bytes; it does not make an unsupported payload acceptable.

For supported payloads, one actual SQLite transaction checks (source, event_id), binds the receipt to the SHA-256 hash of the exact body bytes, inserts a new receipt and updates the local counter. A repeat with identical bytes replays. A different body under the same event key conflicts and preserves the first state. The runner includes a local failure hook for focused transaction tests; it is not reachable from the signed payload or CLI.

The strict byte binding is deliberate. same-event-new-bytes has a fresh valid MAC and the same parsed JSON meaning as the first delivery, yet its receipt conflicts because spacing changed. This is not a claim that whitespace changed the business meaning. It demonstrates that the receiving contract chose byte identity rather than semantic equivalence. Real systems may need a documented canonical representation or another independently validated business payload contract; do not silently invent one after a conflict.

A receiver must not acknowledge a database failure as a completed effect. Likewise, a committed receipt here establishes only a four-unit local counter change. It is not evidence of an external side effect, downstream acceptance, repair or a provider-specific acknowledgement/retry policy.

6. Receive verification evidence as a delivery pod

  • Builder: retain exact runner/runtime versions and original/changed reports. Locate the authenticated fields, actual verification stage and committed receipt/effect evidence; keep keys and MAC values out of shared records.
  • Receiver: reproduce a byte-change rejection, the aged valid-MAC refusal and a freshly signed repeat. Record your own action, observed stage counts and assistance rather than inheriting the builder's result.
  • Source, application and platform owners: agree the real signature scheme/SDK, source-to-key mapping, clock policy, supported event meaning, receipt identity/retention and bounded recovery. Assign key provisioning/rotation, HTTP body preservation, durable isolation and downstream reconciliation to their actual owners.

Each participant keeps an individual record:

Text example
Synthetic alias / role:
Exact runner / Python / SQLite / command:
Prediction before execution:
Observed delivery/time/body hash and evidence path:
MAC result / timestamp decision / receiver parse count:
Receipt identity / before and after local effect:
Changed send time / separate prediction and observation:
Own contribution / assistance:
Receiving action actually attempted / assistance:
Remaining boundary / next bounded test / accountable role:

The exercise does not implement key rotation, secret storage, TLS, HTTP parsing, provider compatibility, end-user authorization, durable replay history or real business permission. Practice evidence does not establish qualification, independence, placement or authority to deploy. Continue with event and receiving semantics and uncertain HTTP outcomes for separate ordering, acknowledgement and reconciliation questions.

7. Defend the contract in an interview

Explain why JSON reserialization can break verification while preserving parsed meaning, and why a freshly re-signed body can still conflict with an earlier receipt. Name the exact equality contract at each boundary: bytes, supported payload, event identity or local effect.

Explain why a valid MAC does not establish freshness, new work or end-user authorization. Use the repeat case to show why signature bytes and delivery-attempt IDs make poor business receipt keys in this specific contract. Identify what a shared-secret MAC cannot prove about a particular individual.

Finally, design the next bounded real-adapter test: preserve the actual provider's raw body and signed fields, verify its supported SDK/configuration, test a duplicate and unsupported event, and retain receipt/effect evidence under the real runtime's durability and concurrency policy. Source-specific identifiers, key rotation, clock skew and acknowledgement behavior must come from that integration's contract.

Sources checked 28 October 2026: OWASP webhook security, Python 3.11 HMAC and constant-time comparison, Stripe's endpoint/signature and duplicate-event guidance, and SQLite transaction behavior. Provider guidance explains real source boundaries; this original envelope does not implement the provider's scheme. The fixtures, comparisons, diagrams and receiving exercise are original.

Before you move on

Keep the evidence of your work.

  • Original and changed-age reports with exact runtime and byte evidence.
  • Separate authentication, freshness, supported payload and receipt decisions.
  • An individual receiving action and next accountable source or application test.

Keep these in your own working copy. Reading this page does not save work or record completion.

Suggested next free session

Enterprise integration & safe change

Connect delivery verification to event order, acknowledgement and receiving-team reconciliation. This is a suggested sequence; choose the practice that addresses your current gap.

Open the next free guide

Free account

Give your practice a home.

Try a role exercise with account drafts and a downloadable worksheet. Keep the files you made in this guide in your own working copy.

Create a free learning account

Guided training · by application

Build toward a program.

Request structured lessons and a training seat. Scope, schedule, feedback arrangements and tuition are confirmed in writing before you commit.

Explore a training application
Have Academy access?

Use your existing training seat to open worked explanations and practice in the related lesson. Creating a free account does not grant a seat.

Open the related Academy lesson →
Back to top ↑