A webhook consumer that “works on the happy path” is easy. A consumer that survives retries, duplicates, out-of-order events, partial failures, and timeouts is the actual goal. This page is a checklist of the patterns that get you there. For the delivery contract, see Retry & delivery. For the event catalog, see Payment events.Documentation Index
Fetch the complete documentation index at: https://developers.bloobank.com/llms.txt
Use this file to discover all available pages before exploring further.
The non-negotiables
1. Verify the signature first
Before parsing, before logging, before anything. Untrusted input is a vulnerability. See Verifying signatures.
2. Deduplicate by messageId
Persist every
messageId you have processed. On receipt, check first — if already seen, ack 200 without reprocessing.3. Respond within 10 seconds
Hard timeout. Do real work asynchronously; ack first.
4. Use data.status / data.ordVersion as truth
Events may arrive out of order. Never trust the event name alone to infer current state.
The idempotent consumer pattern
Two patterns to internalize:Pattern A — synchronous dedupe
If your work is fast (well under the 10-second budget), you can do it inline:messageId arrives twice concurrently, only one passes the insert; the other returns a duplicate-key error and acknowledges without processing.
Pattern B — async dedupe (recommended for any non-trivial work)
For anything that touches the database, calls an external service, or might be slow:Handling out-of-order events
Events on the same order can arrive out of order. The defensive pattern uses the monotonic version field (data.ordVersion):
ordVersion increments on every mutation of the order’s content. Tracking the highest version you have seen makes your local view monotonically consistent, even when delivery order is scrambled.
When to GET vs. trust the payload
The webhook payload reflects the resource’s state at the moment of emission. For most uses, that is fresh enough — trust it. You shouldGET the resource fresh when:
- You need fields that are not in the webhook payload (rare —
datais the full resource). - A long delay elapsed between the event’s
occurredAtand your processing time. - Your business logic depends on the absolute current state, not the state-at-emission.
GET is also useful for reconciliation: at daily intervals, list orders by updatedAt >= yesterday and verify each matches your local record.
Fast-ack patterns
The hard limit is 10 seconds. Strategies to stay well under:| Strategy | When |
|---|---|
| Enqueue + ack (Pattern B above) | Always preferable. Queue can be SQS, Kafka, Redis Streams, or in-process. |
| In-memory async | Lightweight tasks (e.g., update a single row). Risk: process crash loses the event before processing. |
| Inline processing | Only when work is provably bounded (e.g., a CPU-only computation taking < 100 ms). |
- HTTP calls to slow third parties (especially payment networks) inside the handler.
- Synchronous email sends.
- Database transactions that may deadlock.
Replay-safety per event class
A retry can deliver the same event multiple times. Your business logic must be safe to apply more than once.| Event | Replay-safe pattern |
|---|---|
payment_order.created | Upsert into local order table. Second time updates nothing. |
payment_order.success | Mark order as paid. Increment a settled-counter only on first observation; the dedupe table prevents double-counting. |
payment_order.failed | Mark order as failed; alert operator on first observation. |
payment_order.expired | Mark order as expired; release any local hold. |
Observability
Track these per-event and aggregate:| Metric | Why |
|---|---|
| Webhook ack latency (p50, p99) | Detect when you are approaching the 10-second timeout. |
| Webhook ack status code distribution | Identify endpoints returning 4xx/5xx. |
| Signature verification failures (count) | Should be near zero; non-zero is a key drift or attempted abuse. |
Duplicate messageId rate | Some duplicates are expected (retries on transient receiver issues). A spike indicates BlooBank-side retries — usually correlates with downstream slowness. |
| Event-name distribution | Anomalous spikes in payment_order.failed are operationally significant. |
| Worker queue depth | Backlog growth means you cannot keep up — scale workers. |
End-to-end lag (occurredAt → processed time) | The user-visible “did my system see the event yet” metric. |
- Verification failure rate > 0 sustained for 5 minutes (key issue or attack).
- Ack p99 > 3 seconds (heading toward timeout).
- Queue depth grows monotonically for more than 10 minutes.
Common bugs (and their symptoms)
| Bug | Symptom |
|---|---|
| Parsing the body before verifying | Signature mismatch on every delivery. Verify first, parse second. |
| Logging the raw signature header | Secret-adjacent material in logs. Log only the verification result. |
Returning 400 on JSON parse error | Permanently failed delivery in BlooBank’s queue. Use 500 for any error you cannot classify. |
Storing messageId in memory only | After process restart, you reprocess everything. Persist to durable storage. |
| Slow inline work | Intermittent timeouts and BlooBank retries. Move to a worker. |
No timeout in your HTTP client (when you GET the resource in handler) | Cascading slowness — your GET hangs, your ack misses 10 s, BlooBank retries, repeat. Bounded timeout (≤ 3 s) is mandatory. |
| Trusting event-name ordering | Race conditions when events arrive out of order. Use ordVersion. |
Reusing messageId for business id | If two unrelated events accidentally collide on a key your code derived from messageId, you may skip processing. messageId is for dedupe only. |
Endpoint hardening
| Hardening | Why |
|---|---|
| HTTPS only | Mandatory. The platform refuses HTTP. |
| Path obfuscation | A receiver at /webhooks/bloobank-7a8b9c is harder to discover. Not a substitute for signature verification. |
| Rate limit your own endpoint | A misconfigured retry-storm should not overwhelm downstream. Bound concurrency to a known-safe level. |
| Restrict by IP if available | If BlooBank publishes egress IP ranges (check the Dashboard), allowlist them at the edge. Compose with signature verification, do not replace it. |
Next
Verifying signatures
Verify every delivery.
Retry & delivery
The delivery contract.
Payment events
Event catalog.