Skip to main content

Webhooks

TagadaPay pushes processing events to your HTTPS endpoint as they happen: a charge fails, a payout lands, a dispute opens. One partner-level endpoint receives events for all your TPAs — the account field on every event tells you which merchant it belongs to (Stripe Connect semantics). Direct merchants subscribe per-TPA the same way. Requires @tagadapay/node-sdk ≥ 3.8.0.

1. Create an endpoint

With a partner key the endpoint is created at partner scope automatically (events for all your TPAs). With a TPA-pinned merchant key it is scoped to that TPA. enabledEvents accepts exact types (payout.paid), prefixes (payout.*), or '*' for everything. Manage endpoints with list(), retrieve(id), update(id, { url, enabledEvents, status }) and del(id). Set status: 'disabled' to pause deliveries instantly.

2. Verify and handle events

Every delivery is signed with your endpoint’s whsec_… secret (header tagadapay-signature, Stripe-style t=<unix>,v1=<hmac>). Use the SDK helper — it verifies and parses in one call, and throws on a bad signature:
The event envelope:
Event ids are deterministic — if a payment processor redelivers the same underlying webhook to us, you will not receive it twice. Still design handlers to be idempotent on event.id.

Event types

This table is the whole catalog — there is nothing else to subscribe to. An enabledEvents entry that names anything outside it is rejected with 400 invalid_body listing the offenders, so a typo fails at subscription time rather than by silently never delivering. Subscribing to a family is easier than listing names: account.* and document.* cover the onboarding lifecycle and pick up events added later. Nested prefixes such as account.kyc.* work too.
Onboarding events reach partner-owned endpoints regardless of the merchant’s own notification preferences. Those preferences govern the merchant’s in-app and email notifications only; they no longer gate webhook delivery.
charge.* payloads include a card block with PSP-agnostic BIN intelligence (funding credit/debit, segment consumer/business/commercial/government, isCommercial, prepaid, issuer country/name/currency, card product) resolved from Tagada’s own enrichment data — ready for commercial-card surcharging and risk rules without any acquirer-specific configuration. See Card intelligence.

Payload by family

data.object depends on the event family. Every object carries an object discriminator, so branch on data.object.object rather than on the event name when one type can arrive in more than one shape. Onboarding, KYC, contracts, account status (account.application.approved / .declined, account.kyc.*, account.contract.*, account.activated, account.suspended, account.payments_enabled, account.payouts_enabled):
title and body are the merchant-facing wording, not structured fields. Treat these events as a signal and read the merchant’s current state from GET /v1/partner/tpas/{id} for the details (contract, KYC outcome, enabled capabilities). Application submitted (account.application.submitted):
Documents (document.requested, document.uploaded, document.verified, document.rejected):
id is dreq_… for a request raised on the payfac account and edreq_… for one raised on the application; documentType is the label shown to the merchant, not an enum. status is the request’s own state (open for a new request, then uploaded, verified, rejected); reason carries the reviewer’s note on a rejection. document.requested can also arrive with the notification shape above when the request was raised through the merchant notification flow, hence the advice to branch on data.object.object. Requirements (account.requirements.updated, one event per change to the checklist):
Gateway protection (account.gwp.completed):
Charges, refunds, disputes, chargeback alerts, payouts carry the corresponding processing object (charge, refund, dispute, chargeback_alert, payout) with the same fields as the reporting API; see the payout example above and Reporting.

Retries

A delivery that doesn’t get a 2xx within 10 seconds is retried with backoff 1 min → 10 min → 1 h → 6 h → 24 h (6 attempts total), then marked failed. Failures never block other events.

3. Delivery log & replay

Audit exactly what was sent, when, and what your server answered:
Replays create a fresh delivery (the historical row is preserved) with the exact original payload — same event.id, so idempotent consumers stay safe.

Testing your integration

Point a temporary endpoint at a request inspector (e.g. https://webhook.site/…), subscribe to '*', then trigger a test event — the fastest is an on-demand payout or a small refund on a test TPA. Check the delivery log to confirm the 200.