Webhook Payloads
What Are Webhooks
A webhook is how Nimbbl proactively tells your server about a status change — a payment succeeding, a refund completing, a payment link being opened — instead of you having to keep asking. As soon as something happens on the Nimbbl side, we send an HTTP POST with the event details to a URL you've configured, in real time.
Configuration
Webhooks are set up per sub-merchant. Event selection, URL requirements, and versioning are all configured with Nimbbl's help — webhook setup is not currently self-serve from Command Center:
- Events — tell [email protected] which events (payment success, refund failed, payment link opened, and so on) should trigger a call to your URL. See Subscribing to Webhook Events below for where to find the full, current event list for each category.
- URL requirements — the URL must be HTTPS, accept POST requests, be publicly reachable, and return a
200response once you've validated the signature. - One URL per event type — a single URL can handle multiple event types, but each event type maps to only one URL at a time. Route different events to different URLs by configuring them separately.
- Test vs. live — test-mode webhooks only fire for test transactions; live-mode webhooks only fire for live transactions. Confirm with Nimbbl whether your test and live traffic sit on one sub-merchant or two separate ones — the migration guide describes test and live as separate sub-merchants configured independently, and it matters for whether a test-mode
webhook_versionflip affects live traffic. - Payload version — v3 or v4, set per sub-merchant via the
webhook_versionfield. v3 sends the full payload as-is; v4 encodes and signs the payload before sending it.
To get your webhook URL(s) configured or changed, send them to [email protected].
Moving webhook_version to v4 doesn't run both versions side by side — the flip is instant and total for that sub-merchant, and failed deliveries during an unprepared cutover expire after roughly 16 hours with no recovery. Deploy and verify your v4 parsing and signature logic, and prove it in test mode, before requesting the live flip. See Migrating Webhooks from v3 to v4 for the full sequence.
Delivery and Retries
Nimbbl delivers a webhook as an HTTP POST with a JSON body. Your endpoint must respond within 15 seconds; anything else — a non-2xx status, a timeout, or no response — is treated as a failed delivery and queued for retry.
If a delivery fails, Nimbbl retries up to 5 more times (6 attempts total, including the first), on this schedule:
| Attempt | Time since the previous attempt | Time since the first attempt |
|---|---|---|
| 1 (initial) | — | 0s |
| 2 | 25 seconds | 25 seconds |
| 3 | 10 minutes | 10 minutes 25 seconds |
| 4 | 1 hour | 1 hour 10 minutes 25 seconds |
| 5 | 6 hours | 7 hours 10 minutes 25 seconds |
| 6 | 16 hours | 23 hours 10 minutes 25 seconds |
After the sixth attempt fails, Nimbbl does not retry again — there is no manual replay today, so an endpoint that's down for the full ~23-hour window loses that event permanently.
Return HTTP 200 immediately after validating the signature. Process the webhook payload (database updates, fulfillment, notifications) after sending the response. This ensures Nimbbl receives your acknowledgement well within the 15-second window and avoids unnecessary retries.
Idempotency and Ordering
Build your handler assuming both of the following:
- No ordering guarantee. Events can arrive out of the order they occurred in, including across retries. Don't assume a
capture_successwill always arrive after itspayment_authorized, for example — check the current state of the underlying transaction rather than inferring it from delivery order. - No delivery-count guarantee. The same event can be delivered more than once (a retry can succeed on Nimbbl's side after your
200was lost in transit, for instance). Dedupe on the event type plus the relevant transaction ID —transaction_idalone isn't sufficient to dedupe, since several event types can share it (see Pre-auth and Capture for a concrete case).
Signature Validation
Every webhook is signed. v1–v3 signs a concatenation of specific fields; v4 signs the entire compact JSON payload with a single HMAC-SHA256, using your access_secret — never trust a webhook body before validating it. See Validating Payment Response with Signature for the algorithm and code samples in Java, C#, PHP, and Python.
Encryption
If payload encryption is enabled for your sub-merchant, the webhook body is AES-256-GCM encrypted instead of (or alongside) being signed. See Using Encrypted Payloads for key derivation, the IV handling, and decryption code — encrypted delivery is not enabled by default, so contact [email protected] to turn it on.
Subscribing to Webhook Events
Which events exist, and their exact v3/v4 payload shapes, are documented on the three payload-reference pages below. Each page also covers how its events are wrapped and signed in v4.
Payment Webhooks covers the payment lifecycle — authorization, capture, and void. Refund Webhooks covers the three refund states. Payment Link Webhooks covers the payment-link lifecycle and the hold placed when a link is used for pre-authorization. If you're moving a sub-merchant from v3 to v4, see Migrating Webhooks from v3 to v4 for what changes and how to cut over safely.