ReconSync.
Finds the debits whose credit never arrived.

ReconSync watches a transaction stream for debits that never received a matching credit confirmation, and fires a signed advisory reversal webhook back to your system before the regulatory clock runs out.

The system that failed cannot be the system that detects the failure.

If the transaction service crashed mid-flow, its own reconciliation logic crashed with it.

The problem

In a transfer, money leaves the customer's wallet and arrives at the destination. Between them sits a provider API call that can time out, return ambiguously, or succeed silently after the client has already given up. When the credit leg fails and the debit is not reversed, the customer has lost money. Most fintechs fall back to nightly batch reconciliation — which means a customer can be out of pocket for up to 24 hours, far outside any mandated reversal window.

What it is not

ReconSync never moves money.

It observes, detects, records and notifies; your own system performs the reversal. Every webhook payload is explicitly marked "advisory": true, and the receiver is expected to verify against its own ledger before acting.

This boundary is deliberate and load-bearing: even a total compromise of ReconSync — every key stolen, every row rewritten — cannot cause an unauthorised payment. The worst an attacker achieves is noise in a queue.

How it works

Your servicedebit + credit
Ingest APIbounded queue
Correlationmatch the legs
Postgresboth legs
Detection sweepwindow expired?
Your endpointsigned, advisory

A separate process observes both legs and notices when the second one never arrives.

Seeing it

the lifecycle, and the three edges that stop a wrong reversal
  pending_debit  ──credit in window──→  completed

       ├──our ingest had a gap──→  suspect
       │     the missing credit proves nothing (ADR-0004)

       └──window expired──→  orphaned
                                  ├──rail confirms settled──→  completed
                                  │     the money arrived (ADR-0005)
                                  └──confirmed failure──→  reversal_pending

In practice

The rail saysWhat happens
settledCloses as completed. No reversal — the money arrived
failed / not_foundThe orphan is confirmed, with evidence. Reversal proceeds
unknownGoes to suspect for a human. Never a reversal

Decisions that shaped it

Advisory only

Every payload carries "advisory": true. ReconSync cannot move a naira, by construction.

It checks its own blind spots ADR-0004

If ReconSync's ingest had a gap over the window, a missing credit proves nothing — so it goes to suspect, not orphaned.

It asks the rail first ADR-0005

Before any reversal, the payment rail is queried. settled closes the transaction with no reversal at all.

Ambiguity reaches a human

unknown is never resolved by guessing. It becomes suspect and waits.

Partial settlement is understood

A fee is not a shortfall. Several credits summing to the expected amount settle once the total arrives.

Hash-chained audit

Append-only, immutability enforced by trigger, with signed checkpoints. The trail outlives the tenant.

Getting started

Run it
make up && make demo
Send both legs
POST /v1/events/debit   ·   POST /v1/events/credit