> ## Documentation Index
> Fetch the complete documentation index at: https://boxrouter.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Buy and Manage Credits

> Buy credits, find invoices and receipts, request a refund, and top up automatically.

Running sandboxes draw from your prepaid Credits balance.

**Before You Begin:** A [BoxRouter dashboard](https://boxrouter.ai/dashboard) account. For client calls, [install a client](https://boxrouter.ai/docs/quickstart.md) and [set an API key](https://boxrouter.ai/docs/quickstart.md#set-your-api-key) with `credentials:manage`: select **Manage Provider Keys (BYOK)** when you create it. See [key permissions](https://boxrouter.ai/docs/setup.md#key-permissions). In a team workspace, a signed-in person needs the `billing:manage` permission instead; keys use `credentials:manage`.

## Buy Credits

Open **Credits** in the dashboard and choose an amount within the allowed range. Checkout shows the
credits, a 5% processing fee, any sales tax and the total due before you pay. The purchase completes when
the payment is confirmed.

You get the full amount you chose. The fee is rounded up to the cent and isn't refunded. Sandboxes use
credits at the provider's cost, with no markup. Purchased credits don't expire. Promotional credits have no cash value and can't be
refunded.

To close an unpaid Checkout session, use the purchase row in the dashboard. From a client, list
the purchases with `credits.purchases` (CLI: `boxrouter api credits purchases`) and copy the purchase's `id`:

```python Python theme={"theme":"css-variables"}
import json
from boxrouter import BoxRouter

with BoxRouter() as client:
    print(json.dumps(client.api.credits.cancel_purchase("PURCHASE_ID").to_dict(), indent=2))
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  const purchase = await client.api.credits.cancelPurchase({ path: { purchase_id: 'PURCHASE_ID' } });
  console.log(JSON.stringify(purchase, null, 2));
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter credits cancel-purchase --purchase-id PURCHASE_ID
```

Cancelling closes only an unpaid session; a payment that completed stays confirmed.

## Check Your Balance

The dashboard shows your balance under **Credits**. From a client:

```python Python theme={"theme":"css-variables"}
import json
from boxrouter import BoxRouter

with BoxRouter() as client:
    print(json.dumps(client.api.credits.balance().to_dict(), indent=2))
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  console.log(JSON.stringify(await client.api.credits.balance(), null, 2));
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter api credits balance
```

Output

```json theme={"theme":"css-variables"}
{
"mode": "test",
"currency": "usd",
"settled_nanodollars": "0",
"held_nanodollars": "0",
"available_nanodollars": "0",
"purchase_bounds_minor": {
"minimum": 500,
"maximum": 50000
}
}
```

| Field | What It Tells You |
| - | - |
| `available_nanodollars` | What a new sandbox can reserve: the settled balance minus the amount held. |
| `held_nanodollars` | The reservations of your running sandboxes, and of ended ones still waiting for the provider's records. |
| `held_sandbox_count` | How many sandboxes hold that reservation. |
| `mode` | The billing mode of this balance: `test` or `live`. `test` means purchases use test payments and no real money moves. |
| `purchase_bounds_minor` | The smallest and largest purchase, in US cents. |

A nanodollar is one billionth of a US dollar.

## See What a Sandbox Was Charged

A sandbox paid with credits has a **Charge** section in its dashboard drawer, and a `charge` field in
the API. BYOK sandboxes have none: their provider bills you. A sandbox whose reservation was released
without a charge has none either.

| `charge.status` | What It Means |
| - | - |
| `reserved` | The sandbox runs. The reservation is held and nothing is charged yet. |
| `pending` | The sandbox ended and the provider's records are not in. The reservation stays held and no amount shows until they arrive. |
| `charged` | The provider's records set the charge: `amount_nanodollars`, with `returned_nanodollars` back in your balance. |

The charge is the provider's cost, with no markup. The info icon shows its working: the provider's
reported or calculated cost, as `basis` says.

If the provider's records change later, a correction moves the charge. It appears in `corrections` and
never reopens the reservation. A raise takes the difference from your balance; a cut returns it.

Charges and corrections also appear next to your purchases in **Recent Transactions** and the full
history. Download the history as CSV, or list the rows with `boxrouter api credits usage`.

Each sandbox's open reservation and each reservation released without a charge are rows too, marked as
`reserved` or `released`. They lower your available balance until the sandbox is charged or the
reservation is released, but they aren't charges. Each row's `key_id` names the API key behind it.

A sandbox created without `lifetime_seconds` renews its reservation hourly while your balance covers it.
`charge.funding` and `funded_until` tell you how: see [run while funded](https://boxrouter.ai/docs/run-while-funded.md).

## Redeem a Promo Code

Enter a promo code in the dashboard under **Credits** (**Redeem code**). A valid code adds its credit
to your balance at once and appears in the history as **Promo Code**. A code works once for your
workspace; entering it again returns `promotion_already_redeemed` and grants nothing more.
Redeeming needs a signed-in dashboard session, not an API key. Promotional credit has no invoice or
receipt and can't be refunded.

## Find Invoices and Receipts

Every confirmed purchase has an invoice and a receipt. Find the purchase under **Credits**, then
choose **Invoice** or **Receipt** to open its Stripe page, which also downloads either one. The
API's `boxrouter api credits purchases` returns the same links on confirmed purchases.

## Refund Unused Credit

Any signed-in workspace member can request a refund from the purchase row. A refund is available
only when all of these hold:

| Condition | Detail |
| - | - |
| Within 24 hours | Counted from the moment the payment was confirmed. |
| Paid by card or Link | Other payment methods can't be refunded here. |
| Unused credit | Promotional credit counts as spent first, and credit held by running sandboxes isn't refundable. |
| At least 100 cents back | After BoxRouter keeps the original Stripe processing fee. |
| Under the limit | Three refund requests in 24 hours. |

The preview shows the `unused` credit, the retained `fee`, the `tax` Stripe adds to the credit
note, and the `total` returned to the original card.

Accepting a refund removes the refunded amount from your available credit at once and turns off
auto top-up.

Previewing and requesting a refund need a signed-in dashboard session. An API key gets `403`.
The [Refund Policy](https://boxrouter.ai/refunds) covers cases outside self-service, such as
duplicate charges, metering errors, or unauthorized activity.

## Top Up Automatically

To turn it on:

1. Under **Credits**, choose **Enable** on the Auto Top-Up card.
2. Set a threshold, an amount within the purchase bounds, and your saved cards.

Enabling needs a default saved card. Add one in the dashboard through Stripe's hosted page, never
through the API.

Auto top-up compares your **available** balance with the threshold. A new sandbox's reservation
can put you under it before any usage settles.

* **On save.** If the balance is already under the threshold, saving buys the amount immediately.
  The dialog names the amount first. Each top-up charges the amount plus the processing fee and any sales tax.
* **In the background.** When the balance falls under the threshold, it buys once for that dip.
  It buys again only after the balance has been back at or above the threshold.
* **Cards.** Cards are tried in your saved order. If one declines, the next is charged, and
  `last_attempt.fallback_card` names the one that worked.

If every card declines, needs authentication, or none is left, auto top-up turns itself off and
records `disabled_reason`. It never retries automatically. Fix the card, then enable it again.

Read the current settings and the saved cards. Both calls change nothing:

```python Python theme={"theme":"css-variables"}
import json
from boxrouter import BoxRouter

with BoxRouter() as client:
    credits = client.api.credits
    print(json.dumps(credits.auto_top_up().to_dict(), indent=2))
    print(json.dumps(credits.payment_methods().to_dict(), indent=2))
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  const { credits } = client.api;
  console.log(JSON.stringify(await credits.autoTopUp(), null, 2));
  console.log(JSON.stringify(await credits.paymentMethods(), null, 2));
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter api credits auto-top-up
boxrouter api credits payment-methods
```

Turn it on with a threshold and an amount, both in US cents. This example charges your default card at once
if your balance is already under 500 cents (5 dollars). Without a default saved card, the update fails with
`409 payment_method_required`.

```python Python theme={"theme":"css-variables"}
import json
from boxrouter import BoxRouter
from boxrouter.models import UpdateCreditAutoTopUp

with BoxRouter() as client:
    settings = UpdateCreditAutoTopUp(enabled=True, threshold_minor=500, amount_minor=2000)
    print(json.dumps(client.api.credits.update_auto_top_up(body=settings).to_dict(), indent=2))
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  const settings = { enabled: true, threshold_minor: 500, amount_minor: 2000 };
  console.log(JSON.stringify(await client.api.credits.updateAutoTopUp({ body: settings }), null, 2));
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter api credits update-auto-top-up --body '{"enabled": true, "threshold_minor": 500, "amount_minor": 2000}'
```

To change the order in which cards are tried, send every saved card ID exactly once. This example reads the IDs
from `payment-methods` and puts the last card first:

```python Python theme={"theme":"css-variables"}
import json
from boxrouter import BoxRouter
from boxrouter.models import ReorderCreditPaymentMethods

with BoxRouter() as client:
    credits = client.api.credits
    ids = [card.id for card in credits.payment_methods().data]
    order = ReorderCreditPaymentMethods(payment_method_ids=ids[::-1])
    print(json.dumps(credits.reorder_payment_methods(body=order).to_dict(), indent=2))
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  const { credits } = client.api;
  const ids = (await credits.paymentMethods()).data.map(card => card.id);
  const order = { payment_method_ids: ids.reverse() };
  console.log(JSON.stringify(await credits.reorderPaymentMethods({ body: order }), null, 2));
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter api credits payment-methods |
  jq '{payment_method_ids: [.data[].id] | reverse}' |
  boxrouter api credits reorder-payment-methods --body -
```

Re-run `auto-top-up` and `payment-methods` to check `enabled`, `threshold_minor`, `amount_minor` and
the cards' `priority`, when present.

## Get Low-Balance Alerts

An alert works without auto top-up. In the dashboard, tick the low-balance email box in the
Auto Top-Up settings; it uses the auto top-up threshold. Through the API the alert has its own
`low_balance_threshold_minor`, in US cents, and `null` turns it off:

```python Python theme={"theme":"css-variables"}
import json
from boxrouter import BoxRouter
from boxrouter.models import CreditAlertsUpdate

with BoxRouter() as client:
    for level in (1000, None):
        body = CreditAlertsUpdate(low_balance_threshold_minor=level)
        print(json.dumps(client.api.credits.update_alerts(body=body).to_dict(), indent=2))
```

```typescript TypeScript theme={"theme":"css-variables"}
import { BoxRouter } from '@boxrouter/sdk';

const client = new BoxRouter();
try {
  for (const level of [1000, null]) {
    const body = { low_balance_threshold_minor: level };
    console.log(JSON.stringify(await client.api.credits.updateAlerts({ body }), null, 2));
  }
} finally {
  await client.close();
}
```

```bash CLI theme={"theme":"css-variables"}
boxrouter api credits update-alerts --body '{"low_balance_threshold_minor": 1000}'
boxrouter api credits update-alerts --body '{"low_balance_threshold_minor": null}'
```

Output

```text theme={"theme":"css-variables"}
{
"low_balance_threshold_minor": 1000,
"email_delivery": "log_only"
}

{
"low_balance_threshold_minor": null,
"email_delivery": "log_only"
}
```

You get one alert when the available balance falls under the threshold. You get another only after
the balance has recovered to the threshold and fallen again. A separate notice goes out at zero
balance.

`email_delivery` is `configured` when the workspace owner's verified address can receive notices.
`log_only` means the deployment sends no email and notices are only logged, so check your
balance on the Credits page.

## Next Steps

> [card] [Cap Your Spend](https://boxrouter.ai/docs/spend-limits.md)
>
> Cap what a workspace can spend.

> [card] [How Sandbox Cost Works](https://boxrouter.ai/docs/cost.md)
>
> Read what a sandbox cost.

> [card] [Connect a Provider Key](https://boxrouter.ai/docs/provider-keys.md)
>
> Pay with your own provider account instead.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.