Set up auto-reload
The portal.wallets.auto_reload.update operation: POST /api/v1/portal/me/wallets/{walletId}/auto_reload in the REST API and the portal_wallets_auto_reload_update tool in the MCP server.
/api/v1/portal/me/wallets/{walletId}/auto_reloadTurns automatic reloading of one of the signed-in customer's wallets on or off. On: when the balance falls below
below, amount is bought and charged to the card (paymentMethodId, default: the customer's default card)
without the customer present. The amount must be one the brand sells as credits (a pack of that price, or within
a custom-amount pack's range) and the threshold at most the amount, so one reload restores the balance. Off keeps
the settings for next time. Auto-reload turns itself off after 2 declined reloads. Customer portal only
(portal:write).
At a glance
- REST:
POST /api/v1/portal/me/wallets/{walletId}/auto_reload - MCP tool:
portal_wallets_auto_reload_update - Operation ID:
portal.wallets.auto_reload.update - Scope: Brand
- Permission:
portal:write - Risk tier:
write - Dry run: Not supported
- Idempotency:
Idempotency-Keyrequired - Modes: Test and live
- Authentication: A customer signed in to the customer portal (API keys can't call it)
- Since: M9
Request
Path parameters
| Field | Type | Description |
|---|---|---|
walletId | wal_… ID | One of your wallets (wal_…). |
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | Yes | Turn auto-reload on or off. |
below | object | No | Reload when the balance falls below this (at most the reload amount). |
below.amount | string | Yes | Integer micros as a string. |
below.currency | string | Yes | |
amount | object | No | How much each reload buys: an amount the brand sells as credits. |
amount.amount | string | Yes | Integer micros as a string. |
amount.currency | string | Yes | |
paymentMethodId | pm_… ID | No | The card to charge (pm_…); default: your default payment method. |
Response
200 OK with a JSON object:
| Field | Type | Description |
|---|---|---|
id | wal_… ID | |
object | "portal_wallet" | |
currency | string | |
status | one of active, frozen, closed | |
balance | money | All your credits: available, promotional and held. |
available | money | Purchased and included credits you can use. |
promo | money | Promotional credits (used first). |
reserved | money | Credits held for usage in progress. |
upcomingExpiries | array of object | |
upcomingExpiries[].amount | money | |
upcomingExpiries[].expiresAt | date-time | |
autoReload | object or null | |
createdAt | date-time |
Examples
Reload €25 below €5
POST /api/v1/portal/me/wallets/wal_01m4tx5nj8emwbyf7y9vcf48c7/auto_reload
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json
{
"enabled": true,
"below": {
"amount": "5000000",
"currency": "EUR"
},
"amount": {
"amount": "25000000",
"currency": "EUR"
}
}
Response (200):
{
"id": "wal_01m4tx5nj8emwbyf7y9vcf48c7",
"object": "portal_wallet",
"currency": "EUR",
"status": "active",
"balance": {
"amount": "32500000",
"currency": "EUR"
},
"available": {
"amount": "25000000",
"currency": "EUR"
},
"promo": {
"amount": "5000000",
"currency": "EUR"
},
"reserved": {
"amount": "2500000",
"currency": "EUR"
},
"upcomingExpiries": [
{
"amount": {
"amount": "5000000",
"currency": "EUR"
},
"expiresAt": "2026-12-31T23:00:00.000Z"
}
],
"autoReload": {
"enabled": true,
"below": {
"amount": "5000000",
"currency": "EUR"
},
"amount": {
"amount": "25000000",
"currency": "EUR"
},
"paymentMethodId": "pm_01m4tx5nj8emwbyf7y9vcf48cb"
},
"createdAt": "2026-09-20T09:00:00.000Z"
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
validation_failed | 400 | A field is malformed, the threshold or amount is missing to turn it on, the amount isn't one the brand sells as credits, or the threshold is zero or above the amount (details.reason). | Fix the fields listed in details.issues and send the request again. |
resource_not_found | 404 | You have no such wallet (another customer's reads the same). | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
wallet_currency_mismatch | 400 | An 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 | You have no saved card to charge, or the card isn't yours or was removed. | Use a saved payment method (pm_…) of the wallet's customer that isn't detached. Customers save one at a subscription or setup checkout. |
version_conflict | 409 | The wallet changed at the same moment; retry. | Fetch the object again and retry the change. |
livemode_not_allowed | 403 | The portal session is in the other mode than the request. | Use test mode, or credentials for the target's mode. Developers need live access granted by an organization owner or admin. |
permission_denied | 403 | The caller isn't a customer signed in to this brand's portal (operators and API keys use the subscription and wallet operations). | Ask an organization owner or admin for a role or scope that allows this action. |
It can also return these common errors: api_version_unsupported, invalid_json, unsupported_media_type, authentication_required, api_key_invalid, scope_required, idempotency_key_required, idempotency_key_reused, idempotency_in_progress, dry_run_not_supported, rate_limited, internal_error.