What breaks when Airwallex webhooks arrive out of order during payout reconciliation?
Airwallex does not guarantee webhook delivery in the order events were generated. A deposit.settled event can arrive before the deposit.pending event that should precede it, so reconciliation logic that assumes sequential arrival can build a match against incomplete state. Order by each event's created_at timestamp and deduplicate by its id, not by arrival order.
Part of the finance integrations guide.
| Symptom | A deposit shows as settled in Loopfour before its own deposit.pending event has been recorded |
|---|---|
| Root cause | Airwallex webhooks are not delivered in generation order; network retries and parallel delivery can reorder events |
| What Airwallex's own docs say | "Airwallex does not guarantee that events are delivered in the order they were generated" |
| Loopfour's registered event surface | payment_attempt.paid, payment_attempt.settled, deposit.settled, deposit.pending — no payout-specific events are registered |
| What to key reconciliation off instead | The event's created_at timestamp for ordering and its id field for deduplication across retries |
Why webhook order isn't guaranteed
Airwallex's own webhook documentation states plainly that event delivery order is not guaranteed: network retries, parallel delivery workers, and transient failures on Airwallex's side can all cause a later event to arrive before an earlier one. Loopfour's Airwallex integration is event-driven — it has a full webhook handler pipeline (event verification, normalization, matching, registration) registered for four event types (payment_attempt.paid, payment_attempt.settled, deposit.settled, deposit.pending) rather than polling Airwallex's API on a schedule — which makes it fast, but also means reconciliation logic has to be built to tolerate events showing up in any order, not just the order they happened.
What this looks like in practice
A typical incoming-funds lifecycle emits at least two events: deposit.pending when Airwallex first sees the deposit, and deposit.settled once it clears. If the settled event's webhook delivery is retried (a transient timeout on your endpoint, for example) and redelivered after the pending event, or if the two events are processed by parallel workers on Airwallex's side, a reconciliation process that expects "pending" before "settled" can end up trying to match a settlement against a deposit record that doesn't exist yet — or, worse, silently skip the match and leave the settlement unreconciled. The same risk applies to payment_attempt.paid and payment_attempt.settled, the other registered pair.
How to build around it
Two fields on every Airwallex event solve this: created_at, which reflects when the event actually happened (not when it was delivered), and id, which stays the same across every retry of the same event. Sort or key reconciliation state by created_at rather than arrival time, and use id to detect and discard a duplicate delivery of an event you've already processed — Airwallex's own guidance is explicit that endpoints should implement idempotent handling keyed on id, since the same event can be delivered more than once. If an event was missed or processed incorrectly, Airwallex retains events for 30 days and lets you manually re-trigger a specific event's delivery from the dashboard rather than needing to rebuild state from scratch.
Next step
Map the finance workflow with the most exposure and prove the automation path.
Bring the invoice, contract, payment reconciliation, or customer finance workflow you have to defend at audit. Loopfour can map the trigger, controls, integrations, and approval loop.
Worked example
A deposit.settled event that arrives before its own deposit.pending
Airwallex records a deposit as pending at 14:02:01 UTC (event id evt_dep_pending_1, created_at 14:02:01) and marks it settled two seconds later at 14:02:03 UTC (event id evt_dep_settled_1, created_at 14:02:03). A transient timeout on the receiving endpoint causes Airwallex to retry the first event; by the time it successfully redelivers, the settled event has already arrived and been processed. A reconciliation process keyed on arrival order would see 'settled' before 'pending' and either error or silently drop the settlement. Keyed on created_at instead, the two events sort correctly regardless of which one physically arrived first — and if the settled event is somehow delivered twice due to the retry, its unchanged id (evt_dep_settled_1) lets the handler recognize and skip the duplicate.
Frequently Asked Questions
Sources
Related
Integration
Why don't my Stripe payouts tie to the NetSuite bank deposit?
A Stripe payout rarely equals one NetSuite deposit line: Stripe nets many charges, fees, and refunds into one transfer settled days later. Check, in order: the payout isn't unbundled into individual charges, the deposit date doesn't match the transaction date, fees are netted not booked separately, a partial capture created a variance, or the payout is still in Undeposited Funds.
Read moreTopic
Integrations
Every finance automation vendor publishes an integrations page: a grid of logos, a claim of "seamless" connectivity, and not much else. That page answers a marketing question — does this vendor touch…
Read moreDiagnostic
Why doesn't an uncommitted Avalara transaction show up on my sales tax return?
AvaTax only reports committed transactions. Commit status is a flag that determines whether a transaction appears on your compliance reports and gets remitted — uncommitted transactions are treated as provisional and are excluded from liability calculations and filings until someone explicitly commits them.
Read moreDiagnostic
What breaks when a reconciled Plaid transaction changes to modified or removed?
Plaid transaction data isn't immutable. A pending transaction can be removed from the feed entirely and replaced by a different transaction once it posts, and a bank can remove a transaction outright. If a payment was already matched to the old transaction, that match is orphaned the moment the removal comes through, and the replacement has to be found and re-matched — it doesn't update in place.
Read more