Wallets tools
MCP tools for Wallets: wallets_create, wallets_get, wallets_list, wallets_transactions_list, wallets_update.
wallets_create: Open a customer's walletwallets_get: Get a wallet and its balancewallets_list: List walletswallets_transactions_list: List a wallet's transactionswallets_update: Update a wallet's settings or status
wallets_create
Open a customer's wallet. Operation wallets.create · Brand level · permission wallets:write · risk tier write · since M6
Returns the customer's wallet in a currency, opening it when it doesn't exist yet: a customer has one wallet per
currency in each brand and mode (spec §17.1). Top-ups, credit packs and subscription credits open the wallet
themselves; open it here to grant credits (credit_grants.create) before the customer has bought any.
Calling it again returns the same wallet, whatever its status.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
customerId | ID (cus_…) | yes | The customer (cus_…) of the brand. |
currency | string | yes | The wallet's currency (one wallet per customer and currency). |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back. - Plan feature: needs
usage_billing.enabled.
Errors
validation_failed:customerIdorcurrencyis missing or malformed.resource_not_found: This brand has no such customer in this mode.customer_deleted: The customer was deleted.version_conflict: The wallet changed at the same moment (another reservation, capture or grant); retry.scope_required: The request names no brand (an org or entity key withoutOwnrecord-Brand).permission_denied: You can't change this brand's wallets (wallets:write: owners, admins and support).livemode_not_allowed: You have no live-mode access, so you can't use live wallets.entitlement_required: The organization's plan doesn't include usage billing (usage_billing.enabled).
Example
Open a customer's EUR wallet
Arguments:
{
"customerId": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
"currency": "EUR",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"object": "wallet",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"customerId": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
"currency": "EUR",
"status": "active",
"statusReason": null,
"statusChangedAt": null,
"available": {
"amount": "0",
"currency": "EUR"
},
"promo": {
"amount": "0",
"currency": "EUR"
},
"reserved": {
"amount": "0",
"currency": "EUR"
},
"balance": {
"amount": "0",
"currency": "EUR"
},
"lowBalanceThreshold": null,
"lowBalance": false,
"autoReload": null,
"createdAt": "2026-10-14T09:00:00.000Z",
"updatedAt": "2026-10-16T10:15:00.000Z",
"upcomingExpiries": []
}
wallets_get
Get a wallet. Operation wallets.get · Brand level · permission wallets:read · risk tier read · since M6
Returns a wallet with its balances, its settings and the next credits to expire (spec §17.1):
available: spendable purchased, subscription and adjustment credits;promo: spendable promo credits (spent first);reserved: held by open reservations and leases;balance: all three together;upcomingExpiries: the next grants to expire, soonest first, with what is left of each;lowBalanceThresholdandlowBalance, the auto-reload settings (autoReload) and the status.
Amounts are integer micros in the wallet's currency.
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (wal_…) | yes | The wallet (wal_…). |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Read-only: changes nothing;
idempotency_keyis ignored.
Errors
validation_failed:idisn't a wallet ID (wal_…).resource_not_found: This brand has no such wallet in this mode (wallets belong to one brand and one mode).scope_required: The request names no brand (an org or entity key withoutOwnrecord-Brand).permission_denied: You can't read this brand's wallets (wallets:read).livemode_not_allowed: You have no live-mode access, so you can't use live wallets.
Examples
A wallet with promo credits expiring next month
Arguments:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"object": "wallet",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"customerId": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
"currency": "EUR",
"status": "active",
"statusReason": null,
"statusChangedAt": null,
"available": {
"amount": "77000000",
"currency": "EUR"
},
"promo": {
"amount": "5000000",
"currency": "EUR"
},
"reserved": {
"amount": "3000000",
"currency": "EUR"
},
"balance": {
"amount": "85000000",
"currency": "EUR"
},
"lowBalanceThreshold": {
"amount": "10000000",
"currency": "EUR"
},
"lowBalance": false,
"autoReload": null,
"createdAt": "2026-10-14T09:00:00.000Z",
"updatedAt": "2026-10-16T10:15:00.000Z",
"upcomingExpiries": [
{
"grantId": "grant_01m4ty7w2a5qx7mhd9k8w6s0b3",
"source": "promo",
"remaining": {
"amount": "5000000",
"currency": "EUR"
},
"expiresAt": "2026-11-01T00:00:00.000Z"
}
]
}
A wallet with auto-reload
Arguments:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"object": "wallet",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"customerId": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
"currency": "EUR",
"status": "active",
"statusReason": null,
"statusChangedAt": null,
"available": {
"amount": "77000000",
"currency": "EUR"
},
"promo": {
"amount": "5000000",
"currency": "EUR"
},
"reserved": {
"amount": "3000000",
"currency": "EUR"
},
"balance": {
"amount": "85000000",
"currency": "EUR"
},
"lowBalanceThreshold": {
"amount": "10000000",
"currency": "EUR"
},
"lowBalance": false,
"autoReload": {
"enabled": true,
"below": {
"amount": "10000000",
"currency": "EUR"
},
"amount": {
"amount": "50000000",
"currency": "EUR"
},
"paymentMethodId": "pm_01m4ty7w2a5qx7mhd9k8w6s0b6",
"failureCount": 0,
"pendingSince": null,
"lastFailureAt": null,
"disabledAt": null
},
"createdAt": "2026-10-14T09:00:00.000Z",
"updatedAt": "2026-10-16T10:15:00.000Z",
"upcomingExpiries": [
{
"grantId": "grant_01m4ty7w2a5qx7mhd9k8w6s0b3",
"source": "promo",
"remaining": {
"amount": "5000000",
"currency": "EUR"
},
"expiresAt": "2026-11-01T00:00:00.000Z"
}
]
}
wallets_list
List wallets. Operation wallets.list · Brand level · permission wallets:read · risk tier read · since M6
Lists the brand's wallets in the mode of the request, newest first, with their balances (without the upcoming expiries: get a wallet for those).
customerId: one customer's wallets (one per currency);currency,status: only wallets in this currency or with this status.
Paginate with limit (1–100, default 10) and starting_after (the next_cursor of the previous page).
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
limit | integer | no | (1–100) |
starting_after | ID (wal_…) | no | – |
customerId | ID (cus_…) | no | Only this customer's wallets (spec: ?customer=). |
currency | string | no | Only wallets in this currency. |
status | one of active, frozen, closed | no | Only wallets with this status. |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Read-only: changes nothing;
idempotency_keyis ignored.
Errors
validation_failed: A filter or pagination value is invalid.scope_required: The request names no brand (an org or entity key withoutOwnrecord-Brand).permission_denied: You can't read this brand's wallets (wallets:read).livemode_not_allowed: You have no live-mode access, so you can't use live wallets.
Example
A customer's wallets
Arguments:
{
"customerId": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"object": "list",
"data": [
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"object": "wallet",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"customerId": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
"currency": "EUR",
"status": "active",
"statusReason": null,
"statusChangedAt": null,
"available": {
"amount": "77000000",
"currency": "EUR"
},
"promo": {
"amount": "5000000",
"currency": "EUR"
},
"reserved": {
"amount": "3000000",
"currency": "EUR"
},
"balance": {
"amount": "85000000",
"currency": "EUR"
},
"lowBalanceThreshold": {
"amount": "10000000",
"currency": "EUR"
},
"lowBalance": false,
"autoReload": null,
"createdAt": "2026-10-14T09:00:00.000Z",
"updatedAt": "2026-10-16T10:15:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}
wallets_transactions_list
List a wallet's transactions. Operation wallets.transactions.list · Brand level · permission wallets:read · risk tier read · since M6
Lists the ledger transactions that moved the wallet, newest first: top-ups, grants, reservations, captures,
releases and expiries. Each shows the signed change of available, promo and reserved and of the
balance (reserving and releasing move credits inside the wallet, so they don't change the balance), with
the reservation, grant or payment it belongs to.
Paginate with limit (1–100, default 10) and starting_after (a txn_… ID).
Annotations: readOnlyHint: true · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (wal_…) | yes | The wallet (wal_…). |
limit | integer | no | (1–100) |
starting_after | ID (txn_…) | no | – |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Read-only: changes nothing;
idempotency_keyis ignored.
Errors
validation_failed:idisn't a wallet ID, or a pagination value is invalid.resource_not_found: This brand has no such wallet in this mode (wallets belong to one brand and one mode).scope_required: The request names no brand (an org or entity key withoutOwnrecord-Brand).permission_denied: You can't read this brand's wallets (wallets:read).livemode_not_allowed: You have no live-mode access, so you can't use live wallets.
Example
A top-up, then a capture
Arguments:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"limit": 2,
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz"
}
Result output:
{
"object": "list",
"data": [
{
"id": "txn_01m4ty7w2a5qx7mhd9k8w6s0b8",
"object": "wallet_transaction",
"walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"kind": "capture",
"status": "posted",
"memo": "Usage capture",
"available": {
"amount": "0",
"currency": "EUR"
},
"promo": {
"amount": "0",
"currency": "EUR"
},
"reserved": {
"amount": "-1250000",
"currency": "EUR"
},
"balance": {
"amount": "-1250000",
"currency": "EUR"
},
"reservationId": "res_01m4ty7w2a5qx7mhd9k8w6s0b4",
"grantId": null,
"paymentId": null,
"effectiveAt": "2026-10-16T10:16:00.000Z",
"createdAt": "2026-10-16T10:16:00.000Z"
},
{
"id": "txn_01m4ty7w2a5qx7mhd9k8w6s0b7",
"object": "wallet_transaction",
"walletId": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"kind": "topup",
"status": "posted",
"memo": "Credit top-up",
"available": {
"amount": "80000000",
"currency": "EUR"
},
"promo": {
"amount": "0",
"currency": "EUR"
},
"reserved": {
"amount": "0",
"currency": "EUR"
},
"balance": {
"amount": "80000000",
"currency": "EUR"
},
"reservationId": null,
"grantId": "grant_01m4ty7w2a5qx7mhd9k8w6s0b2",
"paymentId": "pay_01m4ty7w2a5qx7mhd9k8w6s0b5",
"effectiveAt": "2026-10-14T09:00:00.000Z",
"createdAt": "2026-10-14T09:00:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}
wallets_update
Update a wallet. Operation wallets.update · Brand level · permission wallets:write · risk tier write · since M6
Changes a wallet's low-balance threshold, its auto-reload settings or its status. Send only what changes;
null removes the threshold or the auto-reload settings.
lowBalanceThreshold:wallet.low_balanceis sent when spending or expiry takes the balance below it, once per dip (again only after the balance is back at or above it).wallet.depletedis sent when it reaches zero.autoReload(spec §14.3): when the balance falls belowbelow, the customer's saved payment method (paymentMethodId) is chargedamountoff-session and the wallet is credited. Enabling it resets the failure count; it turns off by itself after 2 failed reloads.status:frozenstops new reservations and leases at once (reservations made before still settle);closedretires an empty wallet (no credits, no open reservations);activeunfreezes or reopens it. Freezing and closing need areason. Freezing emitswallet.frozen.
Annotations: readOnlyHint: false · destructiveHint: false · idempotentHint: true · openWorldHint: false
Arguments
| Argument | Type | Required | Description |
|---|---|---|---|
id | ID (wal_…) | yes | The wallet (wal_…). |
lowBalanceThreshold | object or null | no | Send wallet.low_balance when the balance falls below this; null removes it. |
autoReload | object or null | no | Auto-reload settings; null removes them. |
status | one of active, frozen, closed | no | frozen stops spending (no new reservations or leases), closed retires an empty wallet, active unfreezes or reopens it. |
reason | string | no | Why the wallet is frozen or closed: required with status: frozen or closed. (1–500 characters) |
entity_id | ID (ent_…) | no | Optional: the brand's legal entity (ent_…); brand_id alone is enough. |
brand_id | ID (brd_…) | no | The brand to act on (brd_…), inside your API key's reach. Needed with an organization or entity key; a brand key acts on its own brand. |
idempotency_key | string | no | A unique string for this change. Retrying with the same key and arguments never acts twice. When left out, the server generates one and returns it in the result's idempotency_key. (1–255 characters) |
dry_run | boolean | no | true: check permissions and rules and compute the result, then roll everything back. Nothing is saved. (default false) |
- Brand: pass
brand_idwith an organization or entity key; a brand key acts on its own brand. - Idempotency: the result returns the
idempotency_keyused, yours or one the server generated. Retry with it: a call with the same key and arguments never acts twice (keys are kept for 24 hours). - Dry run: supported.
dry_run: truechecks permissions and rules and computes the result, then rolls everything back.
Errors
validation_failed: Nothing to change, an amount isn't positive (or a reload amount isn't whole minor units), orreasonis missing when freezing or closing.resource_not_found: This brand has no such wallet in this mode (wallets belong to one brand and one mode).wallet_currency_mismatch: A threshold or reload amount isn't in the wallet's currency.wallet_payment_method_invalid: Auto-reload is enabled with a payment method that isn't the customer's, or was detached.wallet_not_empty: The wallet is closed while it still has credits or open reservations (details.balance,details.open_reservations).wallet_closed: A closed wallet is frozen: reopen it first (status: active).version_conflict: The wallet changed at the same moment (another reservation, capture or grant); retry.scope_required: The request names no brand (an org or entity key withoutOwnrecord-Brand).permission_denied: You can't change this brand's wallets (wallets:write: owners, admins and support).livemode_not_allowed: You have no live-mode access, so you can't use live wallets.
Examples
Warn below €10 and reload €50 below €10
Arguments:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"lowBalanceThreshold": {
"amount": "10000000",
"currency": "EUR"
},
"autoReload": {
"enabled": true,
"below": {
"amount": "10000000",
"currency": "EUR"
},
"amount": {
"amount": "50000000",
"currency": "EUR"
},
"paymentMethodId": "pm_01m4ty7w2a5qx7mhd9k8w6s0b6"
},
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"object": "wallet",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"customerId": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
"currency": "EUR",
"status": "active",
"statusReason": null,
"statusChangedAt": null,
"available": {
"amount": "77000000",
"currency": "EUR"
},
"promo": {
"amount": "5000000",
"currency": "EUR"
},
"reserved": {
"amount": "3000000",
"currency": "EUR"
},
"balance": {
"amount": "85000000",
"currency": "EUR"
},
"lowBalanceThreshold": {
"amount": "10000000",
"currency": "EUR"
},
"lowBalance": false,
"autoReload": {
"enabled": true,
"below": {
"amount": "10000000",
"currency": "EUR"
},
"amount": {
"amount": "50000000",
"currency": "EUR"
},
"paymentMethodId": "pm_01m4ty7w2a5qx7mhd9k8w6s0b6",
"failureCount": 0,
"pendingSince": null,
"lastFailureAt": null,
"disabledAt": null
},
"createdAt": "2026-10-14T09:00:00.000Z",
"updatedAt": "2026-10-16T10:15:00.000Z",
"upcomingExpiries": [
{
"grantId": "grant_01m4ty7w2a5qx7mhd9k8w6s0b3",
"source": "promo",
"remaining": {
"amount": "5000000",
"currency": "EUR"
},
"expiresAt": "2026-11-01T00:00:00.000Z"
}
]
}
Freeze a wallet during a dispute
Arguments:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"status": "frozen",
"reason": "Chargeback opened on the last top-up",
"brand_id": "brd_01jbx4m3v8e9t0s6k2c5n7q1wz",
"idempotency_key": "8d0c5f8e-4b7a-4e43-9b0f-2f6a0c1d9e21"
}
Result output:
{
"id": "wal_01m4ty7w2a5qx7mhd9k8w6s0b1",
"object": "wallet",
"brandId": "brd_01m4tx5nj8emwbyf7y9vcf47x4",
"livemode": false,
"customerId": "cus_01m4ty3c2b5qx7mhd9k8w6s0aa",
"currency": "EUR",
"status": "frozen",
"statusReason": "Chargeback opened on the last top-up",
"statusChangedAt": "2026-10-16T10:15:00.000Z",
"available": {
"amount": "77000000",
"currency": "EUR"
},
"promo": {
"amount": "5000000",
"currency": "EUR"
},
"reserved": {
"amount": "3000000",
"currency": "EUR"
},
"balance": {
"amount": "85000000",
"currency": "EUR"
},
"lowBalanceThreshold": {
"amount": "10000000",
"currency": "EUR"
},
"lowBalance": false,
"autoReload": null,
"createdAt": "2026-10-14T09:00:00.000Z",
"updatedAt": "2026-10-16T10:15:00.000Z",
"upcomingExpiries": [
{
"grantId": "grant_01m4ty7w2a5qx7mhd9k8w6s0b3",
"source": "promo",
"remaining": {
"amount": "5000000",
"currency": "EUR"
},
"expiresAt": "2026-11-01T00:00:00.000Z"
}
]
}