Reconcile Transactions & Fees
A complete picture of financial activity in your Program requires combining three non-overlapping datasets: card transactions, card fees, and wallet transactions. This guide explains how each dataset relates to the others, how to correlate them, and how to verify that your reconciliation balances.
The three datasets
flowchart LR
subgraph Card["Per-card activity"]
TXN["GET /cards/transactions\nSpend events, auth holds,\nclearing, settlement, declines"]
FEE["GET /cards/fees\nFX fees, off-ramp fees,\nATM fees, card fees, etc."]
end
subgraph Wallet["Wallet activity"]
WAL["GET /wallets/transactions\nBank deposits, wallet-to-card\ntransfers, wallet-to-wallet"]
end
TXN -.->|"linked via\nauthRefNum"| FEE
| Dataset | What it contains | What it doesn't contain |
|---|---|---|
GET /cards/transactions | All card-level spend events (authorized, cleared, settled, declined) | Fees, wallet movements |
GET /cards/fees | All fee events associated with card activity | Transaction spend amounts, wallet movements |
GET /wallets/transactions | All wallet-level movements (bank deposits, wallet→card transfers, wallet→wallet transfers) | Card spend transactions, card fees |
A single real-world event may produce records in more than one dataset — for example, a foreign currency purchase produces both a transaction record (in GET /cards/transactions) and an FX fee record (in GET /cards/fees). The two records share an authRefNum for correlation.
Step 1: Define your reconciliation window
Choose a date range and the scope — by cardId, cardholderId, or across the whole Program:
| Scope | Parameters |
|---|---|
| Single card | cardId filter on all three endpoints |
| Single cardholder (all their cards) | cardholderId filter |
| Whole Program for a period | before / after timestamp filters only |
All three endpoints support before and after Unix timestamp parameters. Use the same values across all three queries so the windows align.
Step 2: Query all three datasets
Query each endpoint for your window, paginating as needed (limit / offset):
GET /cards/transactions?cardId={cardId}&after={startTs}&before={endTs}&limit=200&offset=0
GET /cards/fees?cardId={cardId}&after={startTs}&before={endTs}&limit=200&offset=0
GET /wallets/transactions?cardId={cardId}&after={startTs}&before={endTs}
Paginate until
totalis exhausted. Each response includes atotalfield (for transactions and fees). Repeat with incrementingoffsetuntil you've retrieved all records — don't assume a single page covers the full window. Wallet transactions may not be paginated; retrieve all and filter client-side. See Pagination, Filtering & Rate Limits.
Step 3: Correlate fees to transactions
Use authRefNum to join fee records to their associated transaction:
transactions WHERE authRefNum = "AUTH123" → fees WHERE authRefNum = "AUTH123"
This link is the key to attributing fees to the specific transaction that incurred them. For reconciliation, build a composite event record — one transaction entry plus its associated fee entries — per authRefNum.
Fee accumulation rules to keep in mind:
- A foreign-currency transaction may carry both an FX fee and an off-ramp fee (if funded via digital assets)
- A foreign-currency transaction carries a foreign transaction fee instead of (not in addition to) a standard transaction fee — they're disjunctive
- Some fees (card issuance, monthly maintenance, ATM balance enquiry) may have no associated
authRefNum
See Transactions & Fees for the full fee accumulation rules.
Step 4: Reconcile wallet movements separately
Wallet transactions are independent of card transactions — they don't appear in GET /cards/transactions or GET /cards/fees. Query them separately and reconcile against the card's or wallet's balance movements:
- A wallet-to-card transfer (
POST /wallets/transfer,destinationType = 1) should produce an entry inGET /wallets/transactionsand be reflected inGET /cards/{cardId}/balanceas anavailableBalanceincrease - An inbound wallet bank deposit should appear in wallet transactions and increase the wallet balance
Step 5: Balance verification
Verify your reconciled dataset against the actual balance:
GET /cards/{cardId}/balance
→ { "ledgerBalance": 125000, "availableBalance": 112500, "currency": 0 }
The reconciliation check:
Opening balance
+ Sum of inbound card transactions (credits)
+ Sum of wallet-to-card transfers
- Sum of outbound card transactions (debits)
- Sum of fees
= Closing availableBalance
ledgerBalancevsavailableBalancetiming.ledgerBalanceincludes pending amounts (authorized but not yet cleared, or in-process deposits). If your reconciliation window spans a period with pending items, theledgerBalancewill be higher than your reconciled figure. UseavailableBalanceas the reconciliation target for fully settled activity.
Reconciliation flow diagram
flowchart TD
A["Define window:\ndate range + cardId/cardholderId"]
B["GET /cards/transactions\n(paginate to total)"]
C["GET /cards/fees\n(paginate to total)"]
D["GET /wallets/transactions"]
E["Join fees to transactions\nvia authRefNum"]
F["Compile wallet movements\nseparately"]
G["Sum net card activity:\ncredits - debits - fees"]
H["Sum wallet movements\naffecting card balance"]
I["Opening balance +\nnet card activity +\nwallet movements\n= Closing balance"]
J["Compare to\nGET /cards/{id}/balance\navailableBalance"]
K{"Match?"}
L["✅ Reconciled"]
M["❌ Investigate discrepancy\n(pending items? missing page? timing?)"]
A --> B & C & D
B & C --> E
E --> G
D --> F
F --> H
G & H --> I
I --> J
J --> K
K -- "Yes" --> L
K -- "No" --> M
Common discrepancy causes
| Symptom | Likely cause |
|---|---|
Calculated balance higher than availableBalance | Pending outbound authorization holds exist (ledgerBalance would match) |
Calculated balance lower than availableBalance | A deposit or transfer is reflected in availableBalance but not yet in a transaction record |
| Fee record without a matching transaction | Stand-alone fee (e.g., monthly maintenance, issuance) — no authRefNum to join on |
| Transaction without a fee record | Normal for domestic, same-currency transactions |
transStatus = 1 (Pending) transactions in window | Authorization holds that haven't cleared yet — exclude from settled reconciliation or include in a separate pending summary |
What's next
- Daily Reconciliation Job (Recipe — full paginating script)
- Transactions & Fees
- Pagination, Filtering & Rate Limits
