Payout suppliers with confidence—even through refund waves.
Refund waves, FX drift, and migrations shouldn’t break payouts. We release only the proven set— tied to fresh Finance/Compliance approvals— so overpayments stop and audits get simpler.

Who this is for
OTAs, booking platforms, aggregators
Paying tens to hundreds of thousands of suppliers (hotels, airlines, car rentals, agencies) weekly or monthly.
Where pain shows up
Refund waves cause overpayments and clawbacks; multi-currency rounding drift; ERP/PSP mismatches; migration risk; audit burden.
Outcome we target
Only proven payouts release; affected suppliers HOLD with clear reasons until proofs are fresh.
What you get (in plain English)
Overpayment prevention
Refund waves no longer leak into already-exported payouts; impacted suppliers HOLD with a reason.
Deterministic & penny-exact
Integer math with late quantization + carry-ledger; replay equals transcript—no drift.
Cleaner audits & recon
Vendor Bills/Payments carry window_id + output_digest; PSP/ERP tie back instantly.
Before → After (at a glance)
Traditional stack & flow (before)
- ERP: NetSuite, Oracle, SAP for vendor bills + AP
- Rails: Tipalti, Payoneer, bank/ACH/SEPA wires
- Data/compute: bookings in a SQL warehouse or custom ETL
- Close/recs: spreadsheets + BlackLine/Trintech
Refunds often hit after the payout file is exported → overpayments & clawbacks; migrations cause off-by-cent differences; audits rely on spreadsheets.
Overlay architecture (after)
- Deterministic engine: canonical fold order, 128-bit integers, no intermediate rounding.
- Late quantization + carry-ledger: deterministic sub-cent assignment with ≤½-ULP bound.
- Transcript + output_digest: sealed record; replay always yields the same digest.
- Acceptance gate: Finance ACK + Compliance/Tax (optional PSP SPV); reason-coded HOLDs.
- Cross-version equality: migrations promote only when digests match.
How it works (60 seconds)
1) Ingest & compute
Booking/refund events ingested with idempotency; accumulate integers for base rates, commissions, FX.
2) Window close
Weekly/monthly watermark closes; fold in canonical order; quantize once; carry assigned deterministically; seal transcript & digest.
3) Authorize & disburse
Replay equals transcript & proofs are fresh → ALLOW set exports; affected suppliers HOLD with reasons until proofs refresh.
Keep your rails—no rip-and-replace.
Weekly cadence (example)
- Mon–Thu: Ingest & compute deterministically
- Fri 17:00: Window close (watermark, fold, quantize, carry)
- Fri 17:05: Authorize(window_id) — equality + acceptance
- Fri 17:10: Export ALLOW to Tipalti/Payoneer/bank; HOLDs reason-coded
- Next window: Refund adjustments land here (no silent rewrites)
Acceptance policy (example)
Required proofs
- ACK (Finance reserves) — window-level
- CT (Tax/KYC/sanctions) — supplier/cohort-level
- SPV (optional) — PSP/bank receipt
Freshness & quorum
- Freshness: ACK ≤ 24h; CT ≤ 7d; SPV ≤ 24h
- Quorum: 2 of 3 (Finance, Compliance, Ops)
If unmet
- HOLD with reason:
STALE_PROOF,REFUND_PENDING,INSUFFICIENT_QUORUM - Owners alerted; re-check without mutating outputs
Fits your stack
Keep what works
Tipalti, Payoneer, or bank wires for payouts; NetSuite/Oracle/SAP for GL. We run as a pre-release gate.
Add three fields
window_id— identifies the payout windowoutput_digest— ties to the sealed transcript (replay equality)provider_batch_id— PSP/EBP/bank batch reference
One pre-release call
Right before you export the payout file:
POST /authorize { window_id }
→ ALLOW roster + HOLD roster with reason codesPath A — Pay via PSP (Tipalti/Payoneer/bank)
- Export ALLOW set to PSP/bank as usual
- Create/attach
provider_batch_idfrom the rail - Record Vendor Bills/Payments per supplier per window in ERP
Path B — Pay from ERP (EBP)
- Use ALLOW set to build EBP payment file
- Include
window_idin memo; capture EBP batch asprovider_batch_id - Gate release via
Authorize(window_id)before export
Data intake options: S3/GCS/SFTP, read-only DB view, Kafka/Webhook — adapters provided.
Where these fields go
- PSP/rails batch metadata:
window_id,output_digest,provider_batch_id - GL (NetSuite) custom fields:
custbody_payout_window_id,custbody_output_digest,custbody_provider_batch_id,custbody_transcript_url
Technical details (for software engineers)
View endpoints, data contracts & mappings
API surface
Gate release and fetch evidence:
// Verifies replay digest equality + acceptance matrix (freshness & quorum) → Returns: ALLOW roster + HOLD roster with reason codes
// Push CT/ACK/SPV payloads; we verify signatures & freshness and update decisions
// Sealed transcript + output_digest for replay & audit
Data contracts (minimal)
event_id,ts_occurred,supplier_id,currency,amount_minor,source_type(ex: booking|refund|adjustment),external_ref
supplier_id,bank_token,tax_status,residency_country,withholding_rate
// Finance ACK (window-level)
{ "window_id":"2025-09-05/weekly", "reserves_ok":true, "signer":"fin-ops@...", "expires_at":"2025-09-06T00:00:00Z" }
// Compliance/Tax CT (supplier-level)
{ "supplier_id":"HOTEL-4812", "status":"cleared", "expires_at":"2025-09-10T00:00:00Z" }
// SPV (optional, window-level)
{ "window_id":"2025-09-05/weekly", "provider_batch_id":"TIP-33991", "totals_minor": "123456789", "headers_hash":"0x..." }// Add to PSP batch metadata & GL: window_id, output_digest, provider_batch_id
Results teams aim for
Overpayment prevention
Blocks 100% of overpayments from refund waves by HOLDing affected suppliers.
Replay equality ≥ 99.99%
Windows where digest(replay) == digest(transcript).
Same-day time-to-release
Authorized disbursement same-day after window close when proofs are fresh.
Disputes ↓ 40–60%
Transcript-based proofs reduce supplier support burden.
Audit efficiency
Transcripts replace thousands of spreadsheet lookups during audit.
Targets are set together during a pilot; they are goals, not guarantees.
Risks & mitigations
See risks & how we handle them
- Refund tsunami → stale CT proofs: reason-coded HOLD; unaffected suppliers paid on time.
- Migration mismatch: digest mismatch → rollback + bounded exposure.
- Hot suppliers: shard function + dual-write; promote only on digest equality.
FAQ
What if refunds hit after we exported the payout file?
Impacted suppliers are automatically HOLD with a clear reason (REFUND_PENDING). Unaffected suppliers are paid on time. The adjustments post in the next window—no silent rewrites.
Do we change payout rails?
No. Keep Tipalti/Payoneer/bank and your ERP. We add a pre-release gate before funds move.
How do migrations work safely?
We run dual for a canary cohort and only promote when the new logic produces identical digests to the old system for N windows.
Pilot in 30 days
What we’ll ask in discovery
- How often do refunds arrive after your payout cutoff?
- Which rails (Tipalti/Payoneer/bank) and ERP fields do you use today?
- Where do PSP totals vs ERP differ, and by how much?
Next step
Gate one supplier cohort for two windows; measure equality, reasons, and time-to-release.
