event and ignore what you do not handle.
Event catalog
Two events are reserved:
fiat.deposit.created and fiat.deposit.failed. They are part of the catalog but are not currently emitted — current provider flows expose no durable local state to emit them from safely. Build handlers for them; do not wait on them.fiat.deposit.completed is emitted: it fires for the credit side of an internal transfer, so money arriving on one of your accounts from another Sakawave account does reach you.customer.kyc.suspended when an approval is withdrawn, and customer.kyc.approved or customer.kyc.rejected again if a later review changes the outcome. Their data is the customer object plus a kyc object with status, tier, failureReason and resolvedAt. See Identity and verification.
There are no transfer.* events. An outbound transfer reaches your endpoint as a fiat.withdrawal.* event whose relatedOperation describes the transfer.
Envelope
Every request body has exactly two top-level fields.Sakawave-Webhook-Id header — there is no id inside the body.
Financial payloads
Deposits, withdrawals, bill payments, and card transactions carry the posted ledger transaction.amount and fee are strings of minor units; their Formatted counterparts, and converted amounts inside relatedOperation, are JSON numbers in major units.
status is derived from the event name, so fiat.withdrawal.failed always carries status: "failed".
Related operations
relatedOperation.type is transfer, bill_payment, or card_payment.
transfer— reference, destination type, recipient display name, institution name, last four digits, source and destination amounts and currencies, and nullable failure fields. Inbound movements adddirection: "inward",sessionId, andsenderName. Bank resolution is best effort:institutionNameisnullwhen the NIP bank code cannot be resolved.bill_payment— biller and package names, customer name and address, the last four characters of the customer reference, vend status, token data where the biller returns one, and a nullable failure reason.card_payment— card ID, card label, masked PAN, merchant name, and merchant country.
Resource payloads
account.*, account.detail.*, card.*, and customer.* events carry the same sanitized object their REST endpoint returns — byte for byte what a GET would give you.
Provider identifiers, regulated identity values, full card numbers, CVVs, and ledger balances are never added to a webhook payload.
Because the payload is the resource, a resource event can be acted on directly without a follow-up
GET — unlike a financial event, where the money detail sits in relatedOperation and the authoritative object is elsewhere.
eSIM payloads
esim.* events follow the resource rule, with two shapes rather than one.
An order carries both money figures:
price in USD, and paid in whatever currency the account was actually debited. Reconcile a bank statement against paid; reconcile the catalog against price.
profileIds is what makes esim.order.completed actionable: fetch the install details for each and put the QR in front of the customer.
Activation codes are never in a webhook payload. They install a working SIM, so they are only ever handed out by an authenticated request for one specific eSIM.
esim.usage.low and esim.expiry.near fire once per eSIM, not on every usage sync. You can send them straight to a customer without keeping your own “already warned” flag.
Deduplication is per revision
Resource events are staged with an idempotency key built from the event type, the resource id, and a revision taken from the object’supdatedAt. Two updates that produce the same updatedAt are collapsed into one event. A .created event uses a fixed revision, so a resource is only ever announced once.
Ordering and duplicates
Do not assume global ordering. A manual resend can deliver an older event after a newer one. Apply a change only when it is valid for your current state, and treat the REST resource as the source of truth when an event conflicts with it. Deduplication must be atomic with recording receipt. A typical table uses the webhook ID as a unique primary key and stores the event name, received time, processing status, and a body hash. Return2xx for an event you have already processed.