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 objects

ObjectIDWhat it is
HolderholderIdA 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.
WalletW- followed by a ULIDOne balance in one currency for one holder. A holder has at most one wallet per currency. Every wallet operation takes both holderId and walletId.
EntryWE-, WC-, WG- and othersOne movement on a wallet: a card load, a credit, a charge, a refund and so on, including attempts that failed.
CreditWC-Money you add to a wallet.
ChargeWG-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" }
  ]'
FieldRequiredDescription
emailYesThe holder's email.
referenceIdNoYour ID for the person, 1 to 128 characters. Follows the payee reference ID rules.
currency or ledgerAccountIdOne of themThe wallet's currency, or the ledger account that backs the wallet. Name the ledger account when you have more than one in that currency.
taxJurisdictionNoThe 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.email or currency. Results come from an index that can be a few seconds behind.

A wallet's balance is made of three parts:

BalanceWhat it holds
depositUnsettledCard loads that are authorized but not yet settled.
depositSettledCard loads that have settled but are still inside the card refund window.
customerCreditedYour credits, plus card loads older than the refund window.
balanceThe 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 batchId for an existing batch or batch to create one (then paymentCategory is required).
  • Tax withholding and fees apply as for any payout. creditedAmount is what lands in the wallet after them. Once the credit is credited, withholdingAmount, holderFeeShare and identityVerificationAmount itemize what was deducted.
  • The holder must be payable. Otherwise the call returns 409 HOLDER_NOT_PAYABLE and nothing is written.
  • The response is 201 with the credit, normally in payment-created. It becomes credited when its payment completes, or failed with failureCode and failureReason. 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 201 response is charged.
  • It spends settled card loads first, then unsettled card loads, then your credits. spentFrom shows the split.
  • A balance can never go negative. If the wallet does not hold enough, the call returns 409 WALLET_INSUFFICIENT_FUNDS and the charge is recorded as failed.
  • 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 referenceId with the same terms returns the existing record with 200. Use this to retry safely after a timeout or a 5xx.
  • The same referenceId with different terms returns 409 CREDIT_ALREADY_EXISTS or 409 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.amount is in minor units (1500 = $15.00), unlike credits and charges.
  • externalId is required and makes the call idempotent.
  • The refund is queued: requested, then submitted, then reversed or failed. It appears in the history as a refund entry.
  • A load can be refunded only while it is still a card load. Once it has matured into customerCredited, or after a chargeback, it returns 409 DEPOSIT_NOT_REFUNDABLE. More than what is left on the load returns 409 REFUND_EXCEEDS_REMAINING.
  • A failed refund 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:

FieldValues
typedeposit (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.
directioncredit (money in) or debit (money out).
statepending, settled or failed, the same for every type.
statusThe detailed status for the entry's type, for example credited for a transfer or charged for a charge.
openDisputeCountThe number of disputes still open (open, under-review or needs-more-info) on the entry.
detailsFields 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

ErrorOperationMeaningWhat to do
403 WALLET_NOT_ENABLEDInviteYour account has no wallet program.Contact Payment Labs.
Row error INVALID_LEDGER_ACCOUNT, MORE_THAN_ONE_LEDGER_ACCOUNTInviteThe 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_MISMATCHInviteThe holder already has a wallet in that currency under another ledger account.Use the existing wallet.
Row error DUPLICATE_INVITEBulk inviteAnother row in the request has the same email or reference ID.Send each holder once.
400 VALIDATION_ERROR with INVALID_PAYLOADBulk inviteThe body is missing, not an array, or empty.Send 1 to 100 rows.
404 NOT_FOUNDAllThe holder and wallet do not match, or are not yours.Check both IDs.
409 HOLDER_NOT_PAYABLECreditThe holder has not finished onboarding.Retry later with the same referenceId.
400 CREDIT_BATCH_INVALIDCreditThe batch is missing, archived or cannot take this payment.Use another batch.
409 WALLET_INSUFFICIENT_FUNDSChargeThe wallet balance is too low.Charge less, or retry with a new referenceId later.
409 DEPOSIT_NOT_REFUNDABLERefundThe load was never authorized, was charged back, or has matured into customerCredited.The holder withdraws matured money instead.
409 REFUND_EXCEEDS_REMAININGRefundThe amount is more than what is left on the load after spending and earlier refunds.Refund less.
409 CUSTOMER_FROZENCredit, charge, refundYour wallet program is frozen.Contact Payment Labs support.
5xxCredit, chargeA temporary failure.Retry with the same referenceId.

Next steps


What’s Next

Did this page help you?