Top up your credits
The portal.wallets.top_up operation: POST /api/v1/portal/me/wallets/top_up in the REST API and the portal_wallets_top_up tool in the MCP server.
/api/v1/portal/me/wallets/top_upStarts a top-up of the signed-in customer's credits with one of the brand's credit packs: a hosted checkout in
topup mode for the customer (a custom-amount pack takes amount). The customer pays there; tax is quoted as
for any top-up and the brand's risk rules apply. The wallet in the pack's currency is credited when the payment
is confirmed (and created then, for a first top-up); the checkout returns to the portal's wallets page.
Customer portal only (portal:write).
At a glance
- REST:
POST /api/v1/portal/me/wallets/top_up - MCP tool:
portal_wallets_top_up - Operation ID:
portal.wallets.top_up - 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
Body
A JSON object (Content-Type: application/json):
| Field | Type | Required | Description |
|---|---|---|---|
priceId | price_… ID | Yes | One of the brand's credit packs (portal.credit_packs.list). |
amount | object | No | A custom-amount pack: how much to top up, within the pack's minimum and maximum. |
amount.amount | string | Yes | Integer micros as a string. |
amount.currency | string | Yes |
Response
201 Created with a JSON object:
| Field | Type | Description |
|---|---|---|
object | "portal_top_up" | |
url | string | The brand's checkout page for this top-up; it returns to the portal's wallets page. |
expiresAt | date-time |
Examples
Buy the €25 pack
POST /api/v1/portal/me/wallets/top_up
Authorization: Bearer or_test_…
Idempotency-Key: 3f1c9a52-7d0e-4b8f-9a61-0c2d5e8b7f14
Content-Type: application/json
{
"priceId": "price_01m4tx5nj8emwbyf7y9vcf48c9"
}
Response (201):
{
"object": "portal_top_up",
"url": "https://ownrecord.io/c/genno/cs_01m4tx5nj8emwbyf7y9vcf48cc?key=ExampleOnlyNotARealSessionKey000",
"expiresAt": "2026-11-06T09:30:00.000Z"
}
Errors
| Code | HTTP status | When | What to do |
|---|---|---|---|
validation_failed | 400 | A field is malformed. | Fix the fields listed in details.issues and send the request again. |
resource_not_found | 404 | The brand sells no such credit pack (or it was archived). | Check the ID and that your credentials are scoped to the right organization, entity, brand and mode. |
checkout_amount_invalid | 400 | A custom amount is missing, outside the pack's minimum and maximum, or given for a fixed pack. | Give a quantity from 1 to 1,000 for fixed prices; for a custom-amount price give customAmount within the price's minimum and maximum, in whole minor units of its currency, with quantity 1. param names the line item. |
checkout_currency_mismatch | 400 | The custom amount isn't in the pack's currency. | Put prices of one currency in a session; create one session per currency. |
customer_deleted | 400 | Your account was deleted by the brand. | Create a new customer for this buyer. Deleted customers stay readable for the records they belong to, but can't be changed. |
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.