> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.solvimon.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.solvimon.com/_mcp/server.

# Wallet top-ups

How credits get into a wallet after the initial grant: a one-off charge the customer pays for, an amount they choose themselves, or an automatic top-up that fires when the balance runs low.

---

## Why this matters

A customer on credit-based pricing stops being able to use your product the moment their [wallet](/platform-guides/wallets-credits/credit-types-wallets) hits zero. Topping it up is therefore not an admin task you want to be in the middle of: the customer should be able to buy more credits themselves, or never notice the problem because a top-up fires automatically.

Both routes charge an [on-demand pricing item](/platform-guides/products-and-pricing/pricing-plans/on-demand-charges) that grants credits to the wallet. You model the bundle once as a pricing item, and the same item backs the manual purchase, the customer-chosen amount and the automatic top-up.

## How it works

```mermaid
flowchart LR
    A[On-demand pricing item<br />with a wallet grant] --> B[Charge]
    B --> C[One-off invoice]
    C --> D[Payment against<br />the stored method]
    D --> E[Wallet grant booked]
    E --> F[Balance increased]
```

A top-up is always a charge of one or more on-demand pricing items on a [pricing plan schedule](/platform-guides/customers-and-billing/subscriptions/subscription-schedules). The charge produces a one-off invoice, the invoice is paid with a stored payment method, and the wallet grant attached to the pricing item's config is booked against the wallet.

| Route            | Who triggers it                                | Amount                                             |
| ---------------- | ---------------------------------------------- | -------------------------------------------------- |
| One-off charge   | The customer, or your platform                 | Fixed by the pricing item, or chosen within a band |
| Automatic top-up | Solvimon, when the balance crosses a threshold | Fixed by the pricing item, or a configured amount  |

## Charge a top-up

`POST /v1/invoices/charge-on-demand-pricing-items` charges the items and returns the resulting invoice.

**`Charge a credit bundle`**

```bash Charge a credit bundle
curl -X POST https://test.api.solvimon.com/v1/invoices/charge-on-demand-pricing-items \
  -H "X-API-KEY: <apiKey>" \
  -H "Content-Type: application/json" \
  -d '{
    "pricing_plan_schedule_id": "ppsc_example",
    "pricing_items": [
      { "pricing_item_id": "prii_credit_pack_1000" }
    ],
    "payment_method_id": "pmet_example",
    "finalize_immediately": true
  }'
```

**`Response`**

```json Response
{
  "object_type": "INVOICE",
  "id": "invo_example",
  "type": "ONE_OFF",
  "status": "FINAL",
  "customer_id": "cust_example",
  "pricing_plan_schedule_ids": ["ppsc_example"]
}
```

| Field                      | Required | Description                                                                          |
| -------------------------- | -------- | ------------------------------------------------------------------------------------ |
| `pricing_plan_schedule_id` | Yes      | The schedule the on-demand items belong to                                           |
| `pricing_items`            | Yes      | The items to charge, each with a `pricing_item_id` and an optional `flexible_amount` |
| `payment_method_id`        | No       | The stored payment method to charge; omit to leave the invoice to normal collection  |
| `finalize_immediately`     | No       | Finalize the resulting invoice rather than leaving it in draft                       |
| `start_at`                 | No       | When the charge applies; must be in the future                                       |
| `preview`                  | No       | Return the invoice that would be produced without charging anything                  |

Send `preview: true` to price the top-up before the customer commits, which is what an order summary needs. Nothing is charged and no wallet grant is booked.

See [Charge on-demand pricing items](https://docs.solvimon.com/api-docs/transaction-api/invoices/post-invoices-charge-on-demand-pricing-items) for the full schema.

### Let the customer choose the amount

When the pricing item uses [flexible pricing](/platform-guides/products-and-pricing/pricing-plans/flexible-pricing), the customer picks how much to spend within a band and receives credits in proportion. Pass their choice as `flexible_amount` on the item:

**`Charge a customer-chosen amount`**

```bash Charge a customer-chosen amount
curl -X POST https://test.api.solvimon.com/v1/invoices/charge-on-demand-pricing-items \
  -H "X-API-KEY: <apiKey>" \
  -H "Content-Type: application/json" \
  -d '{
    "pricing_plan_schedule_id": "ppsc_example",
    "pricing_items": [
      {
        "pricing_item_id": "prii_flexible_topup",
        "flexible_amount": { "quantity": "50.00", "currency": "EUR" }
      }
    ],
    "payment_method_id": "pmet_example"
  }'
```

The amount is validated against the item's pricing band: the currency has to match the band's currency, and the quantity has to sit between its minimum and maximum. A rejection names the field as `pricing_items.{index}.flexible_amount`, so you can point the customer at the input they got wrong.

### Charge a top-up from the customer portal

The same call exists as `POST /v1/portal/invoices/charge-on-demand-pricing-items`, scoped to the signed-in customer by the portal session and authenticated with the portal access token rather than an API key. The body is identical, so a self-service top-up screen previews with `preview: true` and then charges with the same payload.

## Automatic top-ups

An auto top-up config (`atco_`) is a resource on the wallet. It watches one wallet and charges an on-demand item whenever the balance drops below a threshold, so the customer never runs out mid-usage.

**`Create an auto top-up config`**

```bash Create an auto top-up config
curl -X POST https://test.api.solvimon.com/v1/auto-top-up-configs \
  -H "X-API-KEY: <apiKey>" \
  -H "Content-Type: application/json" \
  -d '{
    "wallet_id": "wall_example",
    "status": "ACTIVE",
    "threshold": { "credits": { "quantity": "100", "credit_type_id": "ctyp_example" } },
    "pricing_plan_schedule_id": "ppsc_example",
    "pricing_item_id": "prii_credit_pack_1000",
    "payment_method_id": "pmet_example"
  }'
```

**`Response`**

```json Response
{
  "object_type": "AUTO_TOPUP_CONFIG",
  "id": "atco_example",
  "wallet_id": "wall_example",
  "status": "ACTIVE",
  "threshold": { "credits": { "quantity": "100", "credit_type_id": "ctyp_example" } },
  "pricing_plan_schedule_id": "ppsc_example",
  "pricing_item_id": "prii_credit_pack_1000",
  "payment_method_id": "pmet_example",
  "created_at": "2026-08-19T14:32:00Z",
  "updated_at": "2026-08-19T14:32:00Z"
}
```

| Field                      | Required    | Description                                                                                                                       |
| -------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `wallet_id`                | Yes         | The wallet to watch                                                                                                               |
| `status`                   | Yes         | `ACTIVE` or `INACTIVE`                                                                                                            |
| `threshold`                | Yes         | An `amount` or a number of `credits`, matching the unit the wallet keeps its balance in. See [Threshold units](#threshold-units). |
| `topup_amount`             | Conditional | Required when the pricing item uses flexible pricing, rejected when it uses a fixed price                                         |
| `pricing_plan_schedule_id` | Yes         | The schedule the on-demand item belongs to                                                                                        |
| `pricing_item_id`          | Yes         | The on-demand item to charge, which must grant credits to the wallet                                                              |
| `payment_method_id`        | Yes         | The stored payment method to charge                                                                                               |

### Threshold units

The threshold is compared against the wallet's balance in the unit that wallet keeps its ledger in, and there is no conversion between a credit type and a currency. Which of `credits` and `amount` to set is therefore decided by the wallet type, not by you:

| Wallet type                                    | Threshold | Has to match                           |
| ---------------------------------------------- | --------- | -------------------------------------- |
| `CREDIT` with `credit.grant_type` of `CREDITS` | `credits` | `credit.credits_grant.credit_type_id`  |
| `CREDIT` with `credit.grant_type` of `AMOUNT`  | `amount`  | `credit.amount_grant.default_currency` |
| `BALANCE`                                      | `amount`  | `balance.default_currency`             |

A credit wallet funded with a monetary amount keeps its ledger in that currency rather than in credits, so it takes an `amount` threshold. Only a wallet type whose credits are granted as credits takes a `credits` threshold.

> **Warning**
>
> A threshold in the other unit is not converted and does not fire. The mismatch is recorded as an error on the platform's logs rather than rejected when you save the config, so a wallet can drain without a single top-up. Check the unit when an auto top-up you expected never arrives.

`topup_amount` is what makes "top up \$50 whenever I drop below 100 credits" expressible without modelling a fixed-price item for every amount you want to offer. It is validated against the same flexible pricing band as a manual charge, at the moment the config is created rather than when it first fires, so a misconfigured band surfaces immediately.

Switch a config on and off with [activate](https://docs.solvimon.com/api-docs/configuration-api/wallets/post-auto-top-up-configs-by-resource-id-activate) and [deactivate](https://docs.solvimon.com/api-docs/configuration-api/wallets/post-auto-top-up-configs-by-resource-id-deactivate) rather than deleting and recreating it, which keeps the history intact:

**`Pause automatic top-ups`**

```bash Pause automatic top-ups
curl -X POST https://test.api.solvimon.com/v1/auto-top-up-configs/atco_example/deactivate \
  -H "X-API-KEY: <apiKey>"
```

List the configs for a wallet with [GET /v1/auto-top-up-configs](https://docs.solvimon.com/api-docs/configuration-api/wallets/get-auto-top-up-configs) and its `wallet_id` filter.

### Manage automatic top-ups from the customer portal

`/v1/portal/auto-top-up-configs` gives the customer the same control over their own wallet: create a config, list the configs for a `wallet_id`, fetch one, and activate or deactivate it. There is no delete, so a customer turning automatic top-ups off deactivates the config and can switch it back on later without re-entering anything.

The wallet grant from an automatic top-up is booked synchronously, so the new balance is visible as soon as the top-up completes rather than after the next asynchronous pass.

## Read a wallet with its top-up rules

`GET /v1/wallets` and `GET /v1/wallets/{id}` accept `expand[]=auto_top_up_configs`, which returns the configs above inline with the wallet instead of as a separate lookup, and `expand[]=wallet_type_id` for the wallet's denomination. Both also work through the customer balance call using the nested key, for example `expand[]=wallet_balances.wallet.wallet_type_id`. See [Read a wallet with its type and top-up rules](/platform-guides/wallets-credits/credit-types-and-wallets#read-a-wallet-with-its-type-and-top-up-rules).

## Show the available top-ups

To render a top-up screen you need the balance and the options in one go. `POST /v1/customers/{id}/wallets/balance` returns the balance of every wallet of a customer and can expand `charge_on_demand_pricing_items`, the on-demand items that can top each wallet up.

**`Get balances with their top-up options`**

```bash Get balances with their top-up options
curl -X POST "https://test.api.solvimon.com/v1/customers/cust_example/wallets/balance?expand[]=wallet_balances.charge_on_demand_pricing_items" \
  -H "X-API-KEY: <apiKey>" \
  -H "Content-Type: application/json" \
  -d '{}'
```

**`Response`**

```json Response
{
  "wallet_balances": [
    {
      "wallet_id": "wall_example",
      "wallet_balance": {
        "balance": { "credits": { "quantity": "240", "credit_type_id": "ctyp_example" } },
        "reserved_balance": { "credits": { "quantity": "0", "credit_type_id": "ctyp_example" } },
        "open_balance": { "credits": { "quantity": "240", "credit_type_id": "ctyp_example" } },
        "balance_at": "2026-08-19T14:32:00Z"
      },
      "charge_on_demand_pricing_items": [
        {
          "pricing_item_id": "prii_credit_pack_1000",
          "pricing_plan_schedule_id": "ppsc_example"
        }
      ]
    }
  ],
  "balance_at": "2026-08-19T14:32:00Z"
}
```

Each entry carries the `pricing_item_id` and the `pricing_plan_schedule_id` you need for the charge call. Add `expand[]=wallet_balances.charge_on_demand_pricing_items.pricing_item_id` to inline the full `pricing_item` as well, which gives you the name and price to render on the button. Without this expansion you would have to resolve the customer's schedules and filter their items yourself.

See [Get wallet balances](https://docs.solvimon.com/api-docs/configuration-api/customers/post-customers-by-resource-id-or-reference-wallets-balance) for the full schema.

## Edge cases

* **The pricing item has to grant credits to that wallet.** An on-demand item without a wallet grant, or one granting to a different wallet type, produces an invoice but no credits.
* **A threshold is denominated like its wallet.** Use `credits` only for a wallet type that grants credits as credits, and `amount` for one funded with a monetary amount, as covered in [Threshold units](#threshold-units). The wrong unit never fires and is not reported back to you.
* **Deactivating is not the same as deleting.** An inactive config stops firing but stays on the wallet, so a customer who turns top-ups back on keeps the same threshold and payment method.
* **A failed payment leaves the invoice behind.** The charge produces the one-off invoice first and pays it second, so a declined payment leaves an unpaid invoice, not a silently dropped top-up. Watch invoice payment status to catch customers whose automatic top-ups are failing.
* **`preview: true` never books anything.** Use it for order summaries; a preview that looks right is not a charge.