Skip to main content

Reconcile payouts

Every payout instruction ends up either matched to a provider record or carrying an explicit, owned exception. Nothing is matched on amount and time alone.

Click any step to read what happens there and what can refuse it.

Uploading a report​

curl -X POST "$API/v1/recon/files" -H "x-api-key: $KEY" -H "x-pehmen-organization: $ORG" \
-H "x-pehmen-environment: test" -H "idempotency-key: $(uuidgen)" -H "content-type: application/json" \
-d '{
"connection_id": "con_…",
"report_type": "GENERIC_PAYOUTS",
"business_date": "2026-09-05",
"csv": "ref,request,amount,currency,status\nTRF-1,po-req-1,1450.00,SGD,PAID\n",
"mapping": { "provider_transaction_ref": "ref", "request_id": "request", "gross_major": "amount", "currency": "currency", "provider_status": "status" }
}'

Amounts in the CSV are in major units and converted at the boundary. An empty report is refused. The response carries the file, whether it was a duplicate, and the match run.

Provider statuses​

Airwallex statusOutcome
PAIDSETTLED
FAILED, CANCELLED, APPROVAL_REJECTEDFAILED
CREATED, IN_APPROVAL, SCHEDULED, PROCESSING, SENTPENDING. SENT means the money left, not that it arrived.
anything elseUNKNOWN

Amended files​

Re-ingesting a changed report for the same date never overwrites. Every active match for that date is superseded first, so a payout is never reconciled twice. A payout the earlier report settled and the amended one omits gets an AMENDED_FILE exception; so does one whose amount or status changed, carrying both readings.

The aging table​

Aging by class is counted server-side over the whole queue: per class and status, how many, how many nobody owns, and the oldest. An unowned column that is not zero is the queue's real problem. The list below it is sorted oldest first for the same reason.