Wallets Overview
Invite wallet holders, read their balances and history, credit their wallets from your funds, and charge them back.
A wallet program gives each of your holders a balance they can spend inside your product. Holders load money with a card, you credit them from your own funds, and you charge them when they spend. This page covers the objects, how money moves in and out, and the operations you use.
Before you start
- The wallet program is enabled per account by Payment Labs. Without it, inviting a holder returns
403 WALLET_NOT_ENABLED. - Every operation on this page needs the Wallet Management permission. See Access to wallets and payment requests.
- Wallets are funded from your ledger accounts. See Funding and Ledger Accounts.
The objects
| Object | ID | What it is |
|---|---|---|
| Holder | holderId | A person in your program. It is the userId of the payee record the invitation creates or finds, so a holder is also one of your payees. |
| Wallet | W- followed by a ULID | One balance in one currency for one holder. A holder has at most one wallet per currency. Every wallet operation takes both holderId and walletId. |
| Entry | WE-, WC-, WG- and others | One movement on a wallet: a card load, a credit, a charge, a refund and so on, including attempts that failed. |
| Credit | WC- | Money you add to a wallet. |
| Charge | WG- | Money you take from a wallet. |
Step 1: Invite holders
Invite one holder with Invite Wallet Holder, or up to 100 with Bulk Invite Wallet Holders.
curl -X POST https://api-sandbox.paymentlabs.io/wallets/invites/bulk \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "email": "[email protected]", "referenceId": "user-1001", "currency": "USD" },
{ "email": "[email protected]", "referenceId": "user-1002", "ledgerAccountId": "A-01JD8ZK3V6M2Q4R7T9W1X3Y5Z7" }
]'| Field | Required | Description |
|---|---|---|
email | Yes | The holder's email. |
referenceId | No | Your ID for the person, 1 to 128 characters. Follows the payee reference ID rules. |
currency or ledgerAccountId | One of them | The wallet's currency, or the ledger account that backs the wallet. Name the ledger account when you have more than one in that currency. |
taxJurisdiction | No | The tax jurisdiction for the holder's payee record. |
Inviting is idempotent. It creates whatever is missing (the payee, the buyer profile the holder pays card loads through, the holder's access and the wallet) and returns 201; inviting an existing holder again returns 200, and created says what, if anything, was added. An invitation email goes only to a person who was not already your payee. The bulk call returns 200 with one result per row; check result on each.
Step 2: Read wallets and balances
- One wallet, live: Get Wallet. Use it before any decision that depends on the balance.
- Many wallets: Search Wallets, filtered for example by
holderId,user.emailorcurrency. Results come from an index that can be a few seconds behind.
A wallet's balance is made of three parts:
| Balance | What it holds |
|---|---|
depositUnsettled | Card loads that are authorized but not yet settled. |
depositSettled | Card loads that have settled but are still inside the card refund window. |
customerCredited | Your credits, plus card loads older than the refund window. |
balance | The total the holder can spend: the sum of the three above. |
Step 3: Credit a wallet
Credit Wallet moves your money into a holder's wallet.
{
"referenceId": "credit-2026-10-0001",
"sourceAmount": "125.50",
"batch": { "name": "October wallet credits" },
"paymentCategory": "royalties"
}- A credit is paid as a virtual payment from the wallet's ledger account. Send
batchIdfor an existing batch orbatchto create one (thenpaymentCategoryis required). - Tax withholding and fees apply as for any payout.
creditedAmountis what lands in the wallet after them. Once the credit iscredited,withholdingAmount,holderFeeShareandidentityVerificationAmountitemize what was deducted. - The holder must be payable. Otherwise the call returns
409 HOLDER_NOT_PAYABLEand nothing is written. - The response is
201with the credit, normally inpayment-created. It becomescreditedwhen its payment completes, orfailedwithfailureCodeandfailureReason. Read it again with Get Wallet Credit, or list a wallet's recent credits with List Wallet Credits.
Step 4: Charge a wallet
Charge Wallet takes money from a holder's wallet back to your ledger account, for example when they buy something in your product.
{ "referenceId": "order-88412", "amount": "19.99", "memo": "Order 88412" }- The charge is immediate: the
201response ischarged. - It spends settled card loads first, then unsettled card loads, then your credits.
spentFromshows the split. - A balance can never go negative. If the wallet does not hold enough, the call returns
409 WALLET_INSUFFICIENT_FUNDSand the charge is recorded asfailed. - Read charges with Get Wallet Charge or list a wallet's recent ones with List Wallet Charges.
List Wallet Credits and List Wallet Charges return only what is among your 100 most recent credits or charges across all your wallets. For a wallet's full history, use Search Wallet Entries filtered on walletId.
Retries: use referenceId
Credits and charges are idempotent on referenceId (1 to 128 characters):
- The same
referenceIdwith the same terms returns the existing record with200. Use this to retry safely after a timeout or a5xx. - The same
referenceIdwith different terms returns409 CREDIT_ALREADY_EXISTSor409 CHARGE_ALREADY_EXISTS. - Without
referenceId, every call creates a new credit or charge.
A charge that failed for lack of funds keeps its referenceId. To try again after the holder adds money, use a new referenceId.
Refund a card load
Refund Wallet Load returns part or all of a holder's card load to their card. The depositId is the entryId of the load's deposit entry in Search Wallet Entries.
{ "externalId": "refund-2026-10-0007", "amount": { "amount": 1500, "currency": "USD" }, "reason": "Customer request" }amount.amountis in minor units (1500= $15.00), unlike credits and charges.externalIdis required and makes the call idempotent.- The refund is queued:
requested, thensubmitted, thenreversedorfailed. It appears in the history as arefundentry. - A load can be refunded only while it is still a card load. Once it has matured into
customerCredited, or after a chargeback, it returns409 DEPOSIT_NOT_REFUNDABLE. More than what is left on the load returns409 REFUND_EXCEEDS_REMAINING. - A
failedrefund can leave the amount out of the wallet. Check the balance with Get Wallet and contact Payment Labs support.
A dispute you accept and resolve with the card-refund outcome also refunds a load; see Wallet Disputes.
Preview a card load
Preview Wallet Load prices a load without creating anything: what reaches the wallet (amount), what the holder's card is charged (payableAmount), and the saved cards and bank accounts the holder can pay with. Send amount in minor units, and amountType charge-value when the amount is what the card should be charged. The holder's fee is payableAmount minus amount.
Read the history
Search Wallet Entries returns every movement on your holders' wallets. Each entry has:
| Field | Values |
|---|---|
type | deposit (a card load by the holder), transfer (your credit), charge (your charge), refund (a card load refunded), chargeback (money taken back by the card network), withdraw (money the holder moved out of the wallet). Accept values you do not know. |
direction | credit (money in) or debit (money out). |
state | pending, settled or failed, the same for every type. |
status | The detailed status for the entry's type, for example credited for a transfer or charged for a charge. |
openDisputeCount | The number of disputes still open (open, under-review or needs-more-info) on the entry. |
details | Fields for the entry's type: the payment and deductions behind a credit, how a charge was spent, the payment behind a withdrawal, the card transaction behind a load, refund or chargeback, the fee and any loss you absorb on a chargeback, and the failure reason of a failed entry. |
There are no webhooks for credits, charges, refunds or balance changes. Read the record, or search entries, to follow them.
Errors and edge cases
| Error | Operation | Meaning | What to do |
|---|---|---|---|
403 WALLET_NOT_ENABLED | Invite | Your account has no wallet program. | Contact Payment Labs. |
Row error INVALID_LEDGER_ACCOUNT, MORE_THAN_ONE_LEDGER_ACCOUNT | Invite | The ledger account is not yours, or you have several in that currency and named none. constraints.validAccounts lists the candidates. | Send ledgerAccountId. |
WALLET_LEDGER_ACCOUNT_MISMATCH | Invite | The holder already has a wallet in that currency under another ledger account. | Use the existing wallet. |
Row error DUPLICATE_INVITE | Bulk invite | Another row in the request has the same email or reference ID. | Send each holder once. |
400 VALIDATION_ERROR with INVALID_PAYLOAD | Bulk invite | The body is missing, not an array, or empty. | Send 1 to 100 rows. |
404 NOT_FOUND | All | The holder and wallet do not match, or are not yours. | Check both IDs. |
409 HOLDER_NOT_PAYABLE | Credit | The holder has not finished onboarding. | Retry later with the same referenceId. |
400 CREDIT_BATCH_INVALID | Credit | The batch is missing, archived or cannot take this payment. | Use another batch. |
409 WALLET_INSUFFICIENT_FUNDS | Charge | The wallet balance is too low. | Charge less, or retry with a new referenceId later. |
409 DEPOSIT_NOT_REFUNDABLE | Refund | The load was never authorized, was charged back, or has matured into customerCredited. | The holder withdraws matured money instead. |
409 REFUND_EXCEEDS_REMAINING | Refund | The amount is more than what is left on the load after spending and earlier refunds. | Refund less. |
409 CUSTOMER_FROZEN | Credit, charge, refund | Your wallet program is frozen. | Contact Payment Labs support. |
5xx | Credit, charge | A temporary failure. | Retry with the same referenceId. |
Next steps
Updated about 24 hours ago
What’s Next
Next: Wallet Disputes.
