Update a wallet's settings or status
The wallets.update operation: PATCH /api/v1/wallets/{id} in the REST API and the wallets_update tool in the MCP server.
PATCH
/api/v1/wallets/{id}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.
At a glance
- REST:
PATCH /api/v1/wallets/{id} - MCP tool:
wallets_update - Operation ID:
wallets.update - Scope: Brand
- Permission:
wallets:write - Risk tier:
write - Dry run: Supported (send
Ownrecord-Dry-Run: true) - Idempotency:
Idempotency-Keyoptional - Modes: Test and live
- Authentication: API key with the
wallets:writescope - Since: M6
Request
Path parameters
| Field | Type | Description |
|---|---|---|
id | wal_… ID | The wallet (wal_…). |
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
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. |
Response
200 OK with a JSON object:
| Field | Type | Description |
|---|---|---|
id | wal_… ID | |
object | "wallet" | |
brandId | brd_… ID | |
livemode | boolean | |
customerId | cus_… ID | |
currency | string | |
status | one of active, frozen, closed | active, frozen (no new reservations or leases) or closed (retired; reopened by a purchase). |
statusReason | string or null | Why the wallet is frozen or closed. |
statusChangedAt | date-time or null | |
available | money | Spendable purchased, subscription and adjustment credits (not reserved). Expired credits stay here until the hourly expiry job takes them out. |
promo | money | Spendable promo credits (spent first; never refundable). |
reserved | money | Held by open reservations and leases. |
balance | money | All unspent credits: available + promo + reserved. |
lowBalanceThreshold | money or null | |
lowBalance | boolean | The balance is below lowBalanceThreshold. |
autoReload | object or null | |
upcomingExpiries | array of object | The next grants to expire (at most 10), soonest first. |
upcomingExpiries[].grantId | grant_… ID | |
upcomingExpiries[].source | one of purchase, subscription, promo, adjustment, refund_reversal | |
upcomingExpiries[].remaining | money | What is left of the grant; whatever is unused at expiresAt expires. |
upcomingExpiries[].expiresAt | date-time | |
createdAt | date-time | |
updatedAt | date-time |
Examples
Warn below €10 and reload €50 below €10
HTTP
PATCH /api/v1/wallets/wal_01m4ty7w2a5qx7mhd9k8w6s0b1
Authorization: Bearer or_test_…
Content-Type: application/json
{
"lowBalanceThreshold": {
"amount": "10000000",
"currency": "EUR"
},
"autoReload": {
"enabled": true,
"below": {
"amount": "10000000",
"currency": "EUR"
},
"amount": {
"amount": "50000000",
"currency": "EUR"
},
"paymentMethodId": "pm_01m4ty7w2a5qx7mhd9k8w6s0b6"
}
}
Response (200):
JSON
{
"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
HTTP
PATCH /api/v1/wallets/wal_01m4ty7w2a5qx7mhd9k8w6s0b1
Authorization: Bearer or_test_…
Content-Type: application/json
{
"status": "frozen",
"reason": "Chargeback opened on the last top-up"
}
Response (200):
JSON
{
"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"
}
]
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
validation_failed | 400 | Nothing to change, an amount isn't positive (or a reload amount isn't whole minor units), or reason is missing when freezing or closing. | Fix the fields listed in details.issues and send the request again. |
resource_not_found | 404 | This brand has no such wallet in this mode (wallets belong to one brand and one mode). | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
wallet_currency_mismatch | 400 | A threshold or reload amount isn't in the wallet's currency. | Use the wallet for this currency. A customer has one wallet per currency (details.expected, details.actual). |
wallet_payment_method_invalid | 400 | Auto-reload is enabled with a payment method that isn't the customer's, or was detached. | Use a saved payment method (pm_…) of the wallet's customer that isn't detached. Customers save one at a subscription or setup checkout. |
wallet_not_empty | 409 | The wallet is closed while it still has credits or open reservations (details.balance, details.open_reservations). | Capture or release its open reservations and wait until its credits are used or expire (details.balance in integer micros, details.open_reservations). To stop spending at once, freeze it instead. |
wallet_closed | 409 | A closed wallet is frozen: reopen it first (status: active). | Reopen the wallet with wallets.update (status: active). A purchase of credits in its currency reopens it, too. |
version_conflict | 409 | The wallet changed at the same moment (another reservation, capture or grant); retry. | Fetch the object again and retry the change. |
scope_required | 400 | The request names no brand (an org or entity key without Ownrecord-Brand). | Call the operation with credentials or a target at its level: an organization, a legal entity, a brand or the platform (details.required_scope). |
permission_denied | 403 | You can't change this brand's wallets (wallets:write: owners, admins and support). | Ask an organization owner or admin for a role or scope that allows this action. |
livemode_not_allowed | 403 | You have no live-mode access, so you can't use live wallets. | Use test mode, or credentials for the target's mode. Developers need live access granted by an organization owner or admin. |
It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, idempotency_key_reused, idempotency_in_progress, approval_required, approval_rejected, approval_expired, stale_approval, approval_policy_denied, rate_limited, internal_error.