← REST APIs: integrate applications and diagnose failures
12 / 12 · 60 MIN

Signed webhooks and local recovery

Check origin and scope, preserve event identity, and separate local commit from delivery to external systems.

Separate delivery from the event

One logical event can arrive repeatedly. A receiver must distinguish each attempt’s envelope from the identity representing the work. Stripe documentation illustrates signatures over the original body and deliveries that may repeat or arrive out of order. The laboratory uses its own contract without the Stripe SDK: X-Lab-Time and X-Lab-Signature authenticate timestamp, a dot, and body bytes with HMAC SHA-256. The key is public teaching material. The time window uses a synthetic clock; signature and freshness do not replace deduplication or producer authorization over resources.

Validate before persisting

The exercise rejects a missing signature, wrong key, modified body, and old envelope before inserting into the inbox. Comparison uses hmac.compare_digest. It then validates schema, types, bounds, and producer scope: the fictional key authorizes only tenant A. A signed event for B1 remains forbidden. Event identity stays stable across attempts. If the same ID returns with different bytes, the teaching contract returns 409 and preserves prior state. This deliberately strict rule requires a real implementation to define payload equivalence and its response to conflicts.

Commit local effects in one transaction

The SQLite database holds objects, inbox, and outbox. BEGIN IMMEDIATE starts the write decision; the inbox unique key and transaction coordinate concurrent requests against this file. A new event may update the object and save notification intent in the outbox before commit. The teaching before-commit fault rolls back all three data sets. Retrying afterward allows the complete operation to run. This design would be insufficient if an external effect had already occurred between those writes. An outbox stores local intent; dispatching, recovering, and reconciling the destination require further work.

Handle order using explicit guarantees

In this fictional contract, each event contains complete state and a monotonic resource version. If version 2 is already applied, version 1 is recorded as stale without changing the amount again. Do not transfer this rule to delta events: ignoring an incremental change can lose work. Also do not assume created or the signature timestamp orders every business change. When the producer lacks the required guarantee, define reconciliation with the authoritative source. Two different IDs of the same type may represent distinct legitimate changes, so deduplicating only by type would be destructive.

Compare observations before and after restart

The preceding lesson’s code sends two concurrent HTTP requests carrying E3. One is accepted and one is a duplicate; object, inbox, and outbox end with the same result as one local acceptance. It then stops the process cleanly, starts another against the same file, and retries E3. The new response remains duplicate and the outbox does not grow. This demonstrates persistence across that restart rather than power loss, replication, or regional recovery. Examine evidence.json and record the actual Python and SQLite versions. The program closes processes and removes the temporary database afterward.

Prepare the operating contract

Before real integration, define identity retention around replay, recovery, and applicable obligations. If the inbox expires before the last permitted replay, an old attempt can produce effects again. Monitoring should distinguish rejected, duplicate, stale, locally accepted, and pending-delivery messages. A 200 does not establish financial completion. This lesson’s workshop asks for an evidence matrix and a plan covering the dispatcher, reconciliation, and authorized destination checks. All banking examples are fictional and do not describe internal BNP Paribas procedures. Independent specialist review remains pending alongside broader integration acceptance work.

OFICINA / WORKSHOP: 40 minutos / 40 minutes
Dados fictícios; não usar APIs bancárias ou credenciais reais.
Fictional data; do not use banking APIs or real credentials.

0-8: Guardar o código da aula anterior como run.py. Executar:
 Save the preceding lesson code as run.py. Execute:
 python3 run.py --output evidence.json
 Registar runtime, sqliteVersion, runnerSha256 e passed.
 Record runtime, sqliteVersion, runnerSha256, and passed.

8-18: Comparar / Compare:
 - raw byte change invalidates signature: 400
 - signed producer outside allowed tenant: 403
 - no untrusted event reached inbox: 0
 Explicar porque assinatura, âmbito e deduplicação são decisões separadas.
 Explain why signature, scope, and deduplication are separate decisions.

18-28: Relacionar / Relate:
 - fault rolled back inbox state and outbox
 - retry after rollback can be accepted
 - concurrent local effects occur once
 - new HTTP process deduplicates replay
 Para cada observação: identidade, estado antes/depois e limite demonstrado.
 For each observation: identity, before/after state, and demonstrated limit.

28-36: Desenhar aceitação do dispatcher sem executar pedidos externos:
 Design dispatcher acceptance without sending external requests:
 timeout após efeito / timeout after effect;
 reenvio com identidade estável / replay with stable identity;
 destino indisponível / unavailable destination;
 retenção e reconciliação / retention and reconciliation.
 Definir responsável, evidência e critério de reabertura para cada caso.
 Define an owner, evidence, and reopening criterion for each case.

36-40: Entregar matriz PT ou EN com observações e trabalho pendente.
 Deliver a PT or EN matrix with observations and remaining work.
 Não afirmar entrega externa, recuperação regional ou autenticação real.
 Do not claim external delivery, regional recovery, or real authentication.
IN PRACTICE

E1 commits version 2 and one outbox intent. Repeated E1 adds no effects; E0 version 1 is recorded without regressing the object.

Common pitfalls

Assuming universal exactly-once execution, ordering by signature, deduplicating by type alone, or confusing an outbox row with confirmed delivery.

Related topics: Requests and outcomes · Caching and pagination · Concurrent changes and verifiable recovery

Take this idea with you

Local evidence must demonstrate identity, atomicity, and recovery; each external system needs an observable delivery contract.

Create account

Reference: Receive Stripe events in your webhook endpoint · HTTP semantics RFC9110; OpenAPI3.2.1; selected primary standards and provider contracts consulted2026-09-30