Applies to Program-Funded programs. See Overview for the bigger picture.
type that says what kind of change you mean, plus one or more entries listing the virtual assets and amounts involved. The whole posting is applied atomically: either all entries land, or none do.
The three types
type | What it does | Typical use |
|---|---|---|
DEPOSIT | Credits the entries to the account’s virtual balance. | User deposits with you, sign-up bonus, cashback, salary, top-up. |
WITHDRAWAL | Debits the entries from the account’s virtual balance. Rejected if any entry would take a balance below zero. | User withdraws from you, expiring a bonus, revoking unspent allowance. |
SETTLEMENT | Debits the entries from the account’s virtual balance and applies their USD value to outstanding card debt. | Closing the loop after card spend. |
POST /postings for the full request and response contract, including settledAmount and per-entry rateSnapshot returned for SETTLEMENT.
Patterns
Most programs fall into one of two shapes. They use the same endpoint and the same operations; what differs is what the balance represents on your side, and which postings you actually need.Custodial mirror
You hold the user’s funds on your side - a custody wallet, an exchange account, a fund — and the virtual balance on Reap mirrors yours so the user can spend on their card.- User deposits with you →
DEPOSITfor the matching asset. - User withdraws from you →
WITHDRAWALfor the same asset. - User’s card spend has cleared →
SETTLEMENTfor the asset they are paying with. Debits their virtual balance and clears the matching card debt in one step.
SETTLEMENT distinct from WITHDRAWAL. After a card swipe clears, you need to pull from the user’s holdings on your side to cover their card spend. A plain WITHDRAWAL for that pull would drop the user’s available balance a second time — once when the card debt accrued, again when the withdrawal posted. SETTLEMENT nets the two against each other so the user’s spending position only moves once and matches what they swiped.
Each entry’s amount is the delta to apply, not the new total. A user depositing 100 USDC becomes a DEPOSIT with entries: [{ virtualAssetId: <USDC>, amount: "100" }]; a later 30 USDC withdrawal becomes a WITHDRAWAL with amount: "30".
Issued credits
You give the user balance — cashback, sign-up bonus, salary allowance — funded by your program rather than the user’s own holdings. Define one virtual asset per credit type (e.g.,BONUS_USD with a FIXED rate of 1.00) and:
- Grant →
DEPOSIT. The user can spend it on their next card swipe. - Revoke unused balance →
WITHDRAWAL.
SETTLEMENT against the granted asset depends on how you want card debt to behave on the account; see the next section.
Settling card debt
SETTLEMENT is how you close card debt against a user’s virtual balance. You debit the user’s balance for the assets they are paying with, and Reap converts those amounts to USD and applies them against the account’s outstanding card debt in the same atomic step.
settledAmount on the response.
When you need it
For custodial mirror programs,SETTLEMENT is essential. It is how you pull from the user’s holdings to cover their card spend without dropping the user’s available balance twice.
For issued-credit programs, SETTLEMENT is optional. The available balance equation assets - liabilities already shows the right number for the user — card debt subtracts from it as soon as a swipe clears, regardless of whether you post anything. Whether you also post SETTLEMENT against the credit depends on how you want the books to read:
- Post
SETTLEMENTto drain the credit’s virtual balance as the user spends. The virtual balance then reflects credit-still-available, andcardDebt.clearedreturns to zero. Cleaner per-asset reporting; more API calls. - Skip
SETTLEMENTand letcardDebt.clearedgrow as the running total of granted-and-spent. The virtual balance reflects credit-ever-granted, and the user’s available position is still correct. Fewer calls; the per-asset view shows totals rather than remaining.
How card debt is drained
Card debt on an account is split into two buckets:pending— authorizations that have been approved but not yet cleared by the network.cleared— cleared transactions awaiting settlement from you.
SETTLEMENT drains cleared first, then any overflow drains pending. You can read the current split from GET /accounts/:id/balance under liabilities.cardDebt.
Card debt can go negative
If you settle more than the user owes (or the user gets a refund after settlement),liabilities.cardDebt.total goes negative. Negative card debt is real credit on the account: it absorbs future card spend before any virtual balance is drained. Reap surfaces the structured split (pending, cleared, total) on the balance endpoint so you can decide how to present it.
Sizing WITHDRAWAL postings
AWITHDRAWAL only checks that its entry does not take the virtual asset’s own balance below zero. It does not check availableBalance. Posting a WITHDRAWAL that ignores outstanding card debt can leave the account with a negative availableBalance — and the master collateral account holding the bag for spend you have already let the user pull off your side.
To size a WITHDRAWAL safely, read the withdrawable field on each row of GET /accounts/:id/assets. It is the maximum amount of that virtual asset you can debit without taking availableBalance below zero. Outstanding card debt — including pending authorizations — is netted out, so the value matches what the account can actually release at the moment of the call.
withdrawable is a per-asset cap, not a budget that adds up across rows. Each value assumes only that asset is being debited and every other balance stays untouched. Example on an account holding 100 USDC virtual and 500 YUSD virtual with 525 of headroom:
- The
USDCrow showswithdrawable: "100.000000"(the full balance — well under the $525 cap). - The
YUSDrow showswithdrawable: "500.000000"(the full balance — same headroom, different asset).
Best practices
Order postings around your actual fund movements
The posting changes Reap’s view of what the user can spend. When the matching change happens on your side matters.DEPOSITonly after on-chain finalization. When the deposit you are mirroring is a chain transfer into your custody, wait for it to be finalized (irreversible), not just confirmed once. If you credit on confirmation and the transfer is later reorged out, you have enabled spend the user cannot back. Plain confirmation depth is not enough on chains that re-org.WITHDRAWALbefore the user withdraws. If you debit Reap after letting the user pull funds, the user can race the withdrawal against a card swipe and overspend. Post theWITHDRAWALfirst sized viawithdrawable; once it returns successfully, you know Reap will reject further spend against that amount, and you can release the funds on your side.SETTLEMENTafter card spend is observable. Settle on theCARD_TRANSACTION_UPDATEDclearing webhook or on a periodic cadence usingliabilities.cardDebt.clearedas the target.
Use the Idempotency-Key header
Every successful posting is a new ledger event, so retries without idempotency double-apply. Pass an Idempotency-Key header with a stable, request-scoped value (UUID v4 is fine) and Reap will return the original result on retry instead of creating a duplicate posting.
One posting per real-world event
A multi-asset deposit on your side becomes oneDEPOSIT posting with multiple entries, not one posting per asset. This avoids brief windows where Reap shows half the deposit applied, and keeps every entry in the same atomic ledger event for audit.
What postings are not
- Not a cap on card spend. Available balance is the cap. Postings feed into available balance but do not block individual transactions on their own.
- Not a log of your internal movements. Only post when you want Reap’s view of the user’s balance to change. Internal transfers between two of your own buckets do not need a posting.
- Not how Reap settles with the card network. That happens automatically against the master collateral account. Postings settle the user side: your obligation to the user, and the user’s spend on Reap.