A wallet is a prepaid credit balance associated with a customer. Wallets allow customers to pre-fund their account, receive credits (promotional or otherwise), and have charges automatically deducted from their balance before falling back to a payment method.
Each customer has a primary wallet created automatically, and can have additional wallets in different currencies.
Key operations:
- Credit - add funds to the wallet (from a payment, promotion, or manual adjustment)
- Debit - remove funds (applied automatically during invoicing, or manually)
- Hold - reserve an amount for a pending charge (released or captured later)
- Transfer - move funds between wallets (e.g., currency conversion)
- Adjust - correct the balance without a formal credit/debit (e.g., migration adjustment)
List wallets
Returns wallets for the org, newest first, cursor-paginated. All filters AND together; multi-value currency is OR within itself. Pass customer_id to scope to one customer; omit to browse org-wide.
Required scope: wallet.read (Read wallets).
query Parameters
created_afterDeprecated: use created_at.gte instead. Removed on 2026-11-30.
created_beforeDeprecated: use created_at.lt instead. Removed on 2026-11-30.
includeExtra data to add to the response: discount_lots. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. discount_lots is sent without being asked until 2026-11-30; after that date it is sent only when named here.
limitPage size (default 25, max 100)
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
customer_idFilter by customer UUID
currencyFilter by ISO 4217 currency code (repeatable or comma-separated; OR within itself)
is_primarytrue=only primary wallets, false=only secondary; omit for both
auto_topup_enabledFilter by auto-topup state
created_at.gteKeep wallets created at or after this instant (RFC 3339).
created_at.ltKeep wallets created before this instant (RFC 3339).
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List wallets › Responses
OK
Create a wallet
Creates a secondary wallet for the specified customer.
Required scope: wallet.write (Operate wallets).
query Parameters
includeExtra data to add to the response: discount_lots. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. discount_lots is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Create a wallet › Request Body
currencyISO 4217 currency the wallet is denominated in; every limit below must be in it.
customer_idCustomer the new wallet belongs to.
Amount charged on each auto-topup, {value, currency}.
auto_topup_enabledWhether the wallet auto-tops-up when its balance drops below low_balance_threshold.
How far a POSTPAID wallet may go negative, {value, currency}; omit for unlimited, 0 for none.
Balance at which a low-balance event (and auto-topup, if enabled) fires, {value, currency}.
Cap on the cash balance, {value, currency}; omit for no cap.
Largest amount a single cash credit may add, {value, currency}; omit for no cap.
Create a wallet › Responses
Created
Get a wallet by ID
Returns a single wallet: its cash and held balances, its currency, its limits and its auto-topup settings. include=discount_lots adds the price-reduction credit it still holds.
Required scope: wallet.read (Read wallets).
path Parameters
idWallet UUID
query Parameters
includeExtra data to add to the response: discount_lots. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. discount_lots is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Get a wallet by ID › Responses
OK
Update a wallet
Merge-updates the wallet's limits and auto-topup settings (absent leaves a field, null removes a limit), and with is_primary: true makes it its customer's primary wallet, demoting the previous one. The primary wallet is what invoice settlement falls back to when the customer holds no wallet in the invoice's currency, the preferred funding source when cross-currency settlement is enabled, and the wallet the customer portal shows billing settings for. Promoting the wallet that is already primary changes nothing. is_primary: false is refused: promote another wallet instead.
Required scope: wallet.write (Operate wallets).
path Parameters
idWallet UUID
query Parameters
includeExtra data to add to the response: discount_lots. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. discount_lots is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Update a wallet › Request Body
auto_topup_amountAmount charged on each auto-topup, {value, currency}; null removes it; omit to leave unchanged.
auto_topup_enabledWhether the wallet auto-tops-up when it drops below low_balance_threshold.
auto_topup_payment_method_idA saved payment method of the wallet's own customer to name for auto-topups; another customer's method is refused. Omit to leave unchanged.
credit_limitHow far a POSTPAID wallet may go negative, {value, currency}; 0 extends no credit, null removes the limit (unbounded overdraft). Omit to leave unchanged.
is_primarytrue makes this wallet its customer's primary wallet and demotes the previous one; false is refused (promote another wallet instead). Omit to leave unchanged.
low_balance_thresholdBalance at which a low-balance event (and auto-topup, if enabled) fires, {value, currency}; null removes the threshold; omit to leave unchanged.
max_balanceCap on the cash balance, {value, currency}; null removes the cap; omit to leave unchanged.
max_single_creditLargest amount a single cash credit may add, {value, currency}; null removes the limit; omit to leave unchanged.
Update a wallet › Responses
OK
Adjust a wallet balance
Applies an adjustment to a wallet balance (positive or negative). When the workspace requires human approval for wallet adjustments (checkpoint wallet.adjust), responds 202 with the pending approval request and applies nothing; an API-key caller is refused 409 APPROVAL_REQUIRED_HUMAN instead.
Required scope: wallet.write (Operate wallets).
path Parameters
idWallet UUID
Headers
Idempotency-KeyIdempotency key for at-most-once processing
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Adjust a wallet balance › Request Body
The amount, {value, currency}, in the wallet's currency: positive on a debit, signed (and never zero) on an adjustment.
sourceWhat originates the movement: one of PAYMENT, REFUND, PROMOTION, INVOICE, CHARGE, FEE, MANUAL, TRANSFER.
Requests also accept the new spelling: each value in lower case (charge for CHARGE). The listed spelling is deprecated in requests and accepted until 2026-11-30.
reasonOptional free-text explanation stored on the ledger entry.
referenceOptional external reference; a repeated (source, reference) is treated as an idempotent replay.
Adjust a wallet balance › Responses
OK
Outcome-dependent: on 200 the posted WalletTransaction; on 202 an ApprovalPending because this workspace requires human approval for wallet adjustments — nothing has been applied yet (follow it with GET /approvals/{id} or the approval.* webhook event whose request_id is approval_request_id).
Credit a wallet
Adds the customer's own money to the wallet's cash balance and answers the ledger transaction (200). MANUAL is money the customer paid outside the product, never a gift. source=PROMOTION with grant_kind=PRICE_REDUCTION instead grants price-reduction credit as a discount lot and answers the lot (201), the same grant POST /wallets/{id}/discount-lots makes; it needs the wallet.grant_promotional permission and a human caller, and a reason. From 2026-11-30 this operation refuses source=PROMOTION and answers only the ledger transaction: grant price-reduction credit with POST /wallets/{id}/discount-lots.
Required scope: wallet.write (Operate wallets).
path Parameters
idWallet UUID
Headers
Idempotency-KeyIdempotency key for at-most-once processing
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Credit a wallet › Request Body
Amount to credit, {value, currency}; the currency must be the wallet's.
sourceWhat originates the credit: one of PAYMENT, REFUND, PROMOTION, INVOICE, CHARGE, FEE, MANUAL, TRANSFER. PROMOTION grants price-reduction credit (goodwill, welcome credit, a make-good) as a discount lot and moves no cash; every other source is the customer's own money entering the cash balance, and MANUAL is money the customer paid outside the product — never a gift.
Requests also accept the new spelling: each value in lower case (charge for CHARGE). The listed spelling is deprecated in requests and accepted until 2026-11-30.
reasonFree-text explanation stored with the credit. Required for PROMOTION: it is printed as the discount reason (BT-139) on every invoice the credit reduces.
referenceOptional external reference; a repeated (source, reference) is treated as an idempotent replay.
expires_atFor PROMOTION credits, when the granted credit lapses; must be in the future. Null = never. Deprecated: use POST /wallets/{id}/discount-lots instead. Removed on 2026-11-30.
grant_kindRequired when source is PROMOTION. PRICE_REDUCTION is the only kind accepted: the credit opens a discount lot that reduces the customer's next invoices before tax, and is never a payment. A reward or referral grant is a different tax treatment and is refused. Deprecated: use POST /wallets/{id}/discount-lots instead. Removed on 2026-11-30.
Requests also accept the new spelling: each value in lower case (price_reduction for PRICE_REDUCTION). The listed spelling is deprecated in requests and accepted until 2026-11-30.
Credit a wallet › Responses
OK
Source-dependent: on 200 the posted WalletTransaction (the customer's own money entered the cash balance); on 201 the DiscountLot a PROMOTION grant opened. The 201 ends on 2026-11-30: grant price-reduction credit with POST /wallets/{id}/discount-lots, and this operation answers the WalletTransaction only.
Debit a wallet
Withdraws funds from a wallet's cash balance, creating a debit transaction. Price-reduction credit (the wallet's discount lots) is not a means of payment: a debit never spends it, so on a PREPAID wallet with no cash the debit is refused with INSUFFICIENT_BALANCE whatever discount lots it holds.
Required scope: wallet.write (Operate wallets).
path Parameters
idWallet UUID
Headers
Idempotency-KeyIdempotency key for at-most-once processing
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Debit a wallet › Request Body
The amount, {value, currency}, in the wallet's currency: positive on a debit, signed (and never zero) on an adjustment.
sourceWhat originates the movement: one of PAYMENT, REFUND, PROMOTION, INVOICE, CHARGE, FEE, MANUAL, TRANSFER.
Requests also accept the new spelling: each value in lower case (charge for CHARGE). The listed spelling is deprecated in requests and accepted until 2026-11-30.
reasonOptional free-text explanation stored on the ledger entry.
referenceOptional external reference; a repeated (source, reference) is treated as an idempotent replay.
Debit a wallet › Responses
OK
List a wallet's discount lots
Lists every grant of price-reduction credit on the wallet, newest first, cursor-paginated: live lots and the ones invoices have drawn down or that expired. remaining says what each still holds.
Required scope: wallet.read (Read wallets).
path Parameters
idWallet UUID
query Parameters
limitPage size (default 50, max 200)
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List a wallet's discount lots › Responses
OK
Grant price-reduction credit
Grants price-reduction credit - goodwill, a welcome credit, a make-good - as a discount lot and answers the lot. It moves no cash and pays nothing: the lot reduces the customer's next invoices in its currency before tax, and each invoice names the lot's reason on the discount it gives. Needs the wallet.grant_promotional permission and a human caller: an API key is refused.
Required scope: wallet.grant_promotional (Grant promotional credit).
path Parameters
idWallet UUID
Headers
Idempotency-KeyIdempotency key for at-most-once processing
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Grant price-reduction credit › Request Body
What the grant puts in, {value, currency}: a gross face value, tax included, in the wallet's currency.
reasonWhy the credit is granted. Printed as the discount reason (BT-139) on every invoice the lot reduces.
expires_atWhen the lot lapses and whatever is left is forfeited; must be in the future. Omit for a lot that never lapses.
referenceOptional external reference; a repeated reference on the same wallet answers the lot it opened.
Grant price-reduction credit › Responses
Created
List wallet holds
Lists the wallet's pending holds, newest first, cursor-paginated.
Required scope: wallet.read (Read wallets).
path Parameters
idWallet UUID
query Parameters
limitPage size (default 50, max 200)
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List wallet holds › Responses
OK
Create a wallet hold
Places a hold on the wallet's cash, reserving the amount for later capture. Only cash less existing holds is available; price-reduction credit cannot fund a hold.
Required scope: wallet.write (Operate wallets).
path Parameters
idWallet UUID
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Create a wallet hold › Request Body
Amount to reserve, {value, currency}; the currency must be the wallet's.
expires_atWhen the wallet auto-releases the hold if it has not been captured.
reasonFree-text explanation of what the hold reserves for.
reference_idIdentifier of the record this hold reserves against; paired with reference_type.
reference_typeType of the record this hold reserves against; paired with reference_id.
reverse_chargeTrue when the captured amount is subject to VAT reverse-charge.
tax_rate^-?\d+(\.\d+)?$Tax rate to apply at capture, as a decimal fraction (0.19 = 19%).
tax_rule_idTax rule to apply when this hold is captured; null for an untaxed hold.
Create a wallet hold › Responses
Created
Make a wallet the customer's primary
Deprecated: use PATCH /wallets/{id} instead. Removed on 2026-11-30.
Promotes this wallet to be its customer's primary wallet and demotes the previous one, atomically. The primary wallet is what invoice settlement falls back to when the customer holds no wallet in the invoice's currency, the preferred funding source when cross-currency settlement is enabled, and the wallet the customer portal shows billing settings for. A customer whose billing currency has changed will still hold a primary wallet in the old currency until this is called - opening a wallet in the new currency does not promote it. Promoting the wallet that is already primary succeeds and changes nothing.
Required scope: wallet.write (Operate wallets).
path Parameters
idWallet UUID
query Parameters
includeExtra data to add to the response: discount_lots. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. discount_lots is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Make a wallet the customer's primary › Responses
OK
Update wallet settings
Deprecated: use PATCH /wallets/{id} instead. Removed on 2026-11-30.
Updates wallet limit and settings fields.
Required scope: wallet.write (Operate wallets).
path Parameters
idWallet UUID
query Parameters
includeExtra data to add to the response: discount_lots. Send a comma-separated list or repeat the parameter. An unknown name is refused with 400. discount_lots is sent without being asked until 2026-11-30; after that date it is sent only when named here.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Update wallet settings › Request Body
auto_topup_amountAmount charged on each auto-topup, {value, currency}; null removes it; omit to leave unchanged.
auto_topup_enabledWhether the wallet auto-tops-up when it drops below low_balance_threshold.
auto_topup_payment_method_idA saved payment method of the wallet's own customer to name for auto-topups; another customer's method is refused. Omit to leave unchanged.
credit_limitHow far a POSTPAID wallet may go negative, {value, currency}; 0 extends no credit, null removes the limit (unbounded overdraft). Omit to leave unchanged.
low_balance_thresholdBalance at which a low-balance event (and auto-topup, if enabled) fires, {value, currency}; null removes the threshold; omit to leave unchanged.
max_balanceCap on the cash balance, {value, currency}; null removes the cap; omit to leave unchanged.
max_single_creditLargest amount a single cash credit may add, {value, currency}; null removes the limit; omit to leave unchanged.
Update wallet settings › Responses
OK
Admin-side wallet top-up
Tops up a customer's wallet with a payment. mode=hosted_checkout answers the organization's hosted checkout URL to share with the customer; mode=off_session charges a saved Stripe card now and answers the charge's status. The wallet is credited asynchronously, when the provider reports the payment succeeded. The answer's state is deprecated: read status.
Required scope: wallet.write (Operate wallets).
path Parameters
idWallet UUID
Headers
Idempotency-KeyUnique key that makes this POST safe to retry: repeats with the same key replay the first response instead of re-executing. Replays are scoped to the retrying principal (same API key / user) and kept for 24 hours. Required on every POST.
Client-generated idempotency key (e.g. a UUID).
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Admin-side wallet top-up › Request Body
Top-up amount, {value, currency}, in the wallet's currency.
modeHow the money is collected: hosted_checkout (default) answers a checkout URL to share with the customer; off_session charges a saved card now.
payment_method_idThe customer's saved Stripe payment method to charge. Required for mode=off_session.
Admin-side wallet top-up › Responses
Created
List wallet transactions
Lists transactions for a wallet with cursor-based pagination.
Required scope: wallet.read (Read wallets).
path Parameters
idWallet UUID
query Parameters
limitPage size. Values above the server-side cap are clamped (standard default 50, cap 200; a few document-heavy lists use larger windows). Invalid values fall back to the default.
cursorOpaque continuation token from the previous response's pagination.cursor. Omit for the first page. Cursors are stateless and do not expire, but are only valid for the list and filters that produced them.
Headers
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
List wallet transactions › Responses
OK
Transfer funds between wallets
Moves funds from the path wallet (source) into the body's target_wallet_id atomically: one database transaction locks both wallets and records a debit transaction on the source and a credit transaction on the target, linked by a shared transfer_id. amount is in the source wallet's currency and is drawn from its cash balance only - price-reduction credit never moves; cross-currency transfers convert through the org's FX rate (the snapshot used is returned) and fail when no rate is configured. Transfers to the same wallet are rejected, and replaying an Idempotency-Key yields an already-processed conflict rather than a second transfer.
Required scope: wallet.write (Operate wallets).
path Parameters
idSource wallet UUID
Headers
Idempotency-KeyIdempotency key for at-most-once processing
X-Workspace-IdSelects the workspace this request operates in, by workspace UUID or slug (e.g. a sandbox workspace for test integrations). Omitted: the organization's default live workspace. Unknown workspace: 404; workspace outside your organization: 403. Discover workspaces via GET /workspaces.
Workspace UUID or slug.
Transfer funds between wallets › Request Body
Transfer amount as {value, currency}. currency must equal the SOURCE (path) wallet's currency; the target leg converts via the org's FX rate when currencies differ.
target_wallet_idWallet that receives the transfer; must belong to the same organization as the source wallet.
reasonOptional free-text explanation stored on both ledger legs of the transfer.
Transfer funds between wallets › Responses
OK