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

# Top up by API

> Let your programs and agents add credits, read their runway and manage auto-recharge by API, MCP, CLI or SDK, within caps you set.

A program or agent that runs long work can keep its own balance topped up: it reads how long the balance lasts, adds credits when it runs low, and manages [auto-recharge](https://runcrate.ai/docs/billing/auto-recharge.md). A top-up charges the workspace's saved default card, the one a person added in the dashboard. Cards are never added or changed by API.

## Turn on Top-ups

No key or agent sign-in can top up until a workspace **owner or manager** turns on its **Top-ups** (in the API, the billing permission):

**1. Open API keys**

Go to **Dashboard > API keys**.

**2. Pick the key or sign-in**

Open the menu of an API key, or of an agent sign-in (MCP or CLI) in the agent sign-ins list, and choose **Top-ups**.

**3. Set the caps**

Turn the permission on and set its **daily** and **monthly** caps (from $20; $100 a day and \$500 a month to start). Save.

Each key or sign-in has its own caps. A day and a month are counted in UTC, and only money actually charged or being charged counts: declined and cancelled top-ups do not. Turning Top-ups off, or the person who turned them on leaving the owner or manager role, stops the key from charging at once; turning them off also turns off auto-recharge that key set. The workspace's default key, inference-only keys and turned-off keys cannot get Top-ups.

## Add credits

An amount from $20 to $10,000 (dollars and cents), with an idempotency key that names the top-up:

**Ask your agent**

Connect the [Runcrate MCP server](https://runcrate.ai/docs/mcp/setup.md), then ask in plain words:

```text wrap theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
Top up the balance by $50 if less than a day of runway is left.
```

The agent reads `billing_balance` and calls `top_up` with `amount_usd: 50`. The MCP server derives the idempotency key from the tool call, so a call its client retries never charges twice. On `requires_action` it gives you the `confirm_url`. See [MCP tools](https://runcrate.ai/docs/mcp/tools.md#billing).

**CLI**

```bash wrap theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
# Charge $50 to the default card; prints the idempotency key it made
runcrate billing topup 50

# Re-send the same top-up to read its status
runcrate billing topup 50 --idempotency-key K
```

**TypeScript**

```typescript wrap theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
// Makes a UUID idempotency key when you give none, and returns it
const topUp = await rc.billing.topUp({ amountUsd: 50 });
if (topUp.status === 'requires_action') console.log(topUp.confirmUrl);
```

**Python**

```python wrap theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
# Makes a UUID idempotency key when you give none, and returns it
topup = client.billing.top_up(amount_usd=50)
if topup.status == "requires_action":
    print(topup.confirm_url)
```

**cURL**

```bash wrap theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
curl -X POST https://www.runcrate.ai/api/v1/billing/topups \
  -H "Authorization: Bearer $RUNCRATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount_usd": 50, "idempotency_key": "nightly-1" }'
```

```json theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
{
  "data": {
    "id": "8d0c7b1e-3f2a-4e5b-9c8d-1a2b3c4d5e6f",
    "status": "succeeded",
    "amount_usd": 50,
    "idempotency_key": "nightly-1",
    "invoice_number": "RC-000123",
    "balance_usd": 104.12,
    "created_at": "2026-10-26T10:00:00Z"
  }
}
```

The key may also go in an `Idempotency-Key` header; if you send both, they must match.

### Idempotency

The key names one top-up. Sending the same key again never charges twice: it answers the same top-up with its **current** status, with the header `Idempotent-Replayed: true`. That is also how you read a top-up later: re-send the same request. Use a new key for a new top-up; the same key with another amount is refused with `409 IDEMPOTENCY_KEY_REUSED`.

Use any 1 to 255 printable characters without spaces, such as a UUID made for each top-up. Keep the key until you know the outcome, and never retry with a new key after a timeout.

### Statuses

Every answer is `200`, with one of four statuses:

**succeeded**

Paid and credited. `balance_usd` is the new balance and `invoice_number` the invoice. Nothing to do.

**processing**

The payment is going through and is not credited yet. Re-send the same request in a minute to read it, or wait for `topup.succeeded`.

**requires\_action**

The bank wants the card holder to confirm. **Nothing is charged yet.** A person opens `confirm_url`, signed in to Runcrate as an owner or manager of the workspace, and confirms before `expires_at`. Then re-send the same request to read the result.

**declined**

Nothing was charged. `reason` (`card_declined`, `payment_blocked` or `authentication_required`) and `message` say why. Tell a person: the default card may need replacing in **Billing > Payment methods**.

An agent that gets `requires_action` should give its user the `confirm_url` link; it cannot confirm the payment itself.

### Errors

Errors use the usual envelope, `{ "error": { "code", "message", "details" } }`, and charge nothing. The codes, each with its HTTP status:

**BILLING\_PERMISSION\_REQUIRED** `403`

This key or sign-in does not have the billing permission. An owner or manager turns it on (see above).

**TOPUP\_CAP\_REACHED** `402`

This top-up would pass a cap. `details` has `cap` (`daily`, `monthly` or `new_workspace`), `cap_usd`, `spent_usd`, `remaining_usd` and `resets_at`.

**NO\_PAYMENT\_METHOD** `402`

The workspace has no saved default card. A person adds one in the dashboard.

**PAYMENT\_HOLD** `402`

Card payments for this workspace are paused while a card dispute is open. A person pays by bank transfer in the dashboard instead.

**IDEMPOTENCY\_KEY\_REUSED** `409`

The key was used for a top-up of another amount. Use a new key.

**conflict** `409`

A top-up with this key, or another card payment of the workspace, is being charged right now. Re-send the same request after `Retry-After` seconds.

**validation\_error** `422`

The amount or key is not valid.

**rate\_limited** `429`

Too many requests. Wait and re-send the same request.

## Read the runway

`GET /api/v1/billing/balance` tells a program when to top up:

| Field | Meaning |
| - | - |
| `creditsBalance` | Credits in the workspace, in USD |
| `activeUsageCost` | What running instances have used and not yet billed |
| `floating_balance_usd` | The balance net of running instances' usage |
| `burn_usd_per_hour` | What the running instances cost together, per hour |
| `runway_hours` | Hours the floating balance lasts at that rate; `null` when nothing runs |

## Auto-recharge by API

Read the workspace's [auto-recharge](https://runcrate.ai/docs/billing/auto-recharge.md) with any key or sign-in:

```bash theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
curl https://www.runcrate.ai/api/v1/billing/auto-recharge \
  -H "Authorization: Bearer $RUNCRATE_API_KEY"
```

It answers `enabled`, `threshold_usd`, `target_usd`, and `last_failure_reason` and `last_failure_at` when the last automatic top-up failed.

Change it with `PUT`. This needs the billing permission, and the person behind the key or sign-in must be an owner or manager:

```bash theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
curl -X PUT https://www.runcrate.ai/api/v1/billing/auto-recharge \
  -H "Authorization: Bearer $RUNCRATE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "threshold_usd": 25, "target_usd": 100 }'
```

`threshold_usd` ($20 to $5,000) and `target_usd` ($50 to $10,000, above the threshold) are required, even to turn it off, as in the dashboard. A `target_usd` above the key's daily cap is refused with `402 TOPUP_CAP_REACHED`. Without a saved card, turning it on answers `402 NO_PAYMENT_METHOD`. Turning it off is never refused for the cap.

Auto-recharge turned on or changed by API counts against that key's or sign-in's caps, like its own top-ups: each recharge is cut down to what is left of today's and this month's cap, and skipped (nothing charged, auto-recharge stays on) when less than \$20 is left. If the key loses the billing permission, or the person who granted it is no longer an owner or manager, auto-recharge turns off with `last_failure_reason` `billing_permission_removed` until a person saves it in the dashboard. Saving auto-recharge in the dashboard takes it off the key's caps.

## Payment methods

`GET /api/v1/billing/payment-methods` lists the saved cards (`brand`, `last4`, `exp_month`, `exp_year`, and which is the `default`), so a program can tell whether a top-up can work. It needs the billing permission, or a key of an owner or manager. The list is read only: cards are added, removed and made default only in the dashboard, in **Billing > Payment methods**.

## Webhooks

[Webhooks](https://runcrate.ai/docs/webhooks/overview.md) tell you how top-ups end, whatever started them:

| Event | When |
| - | - |
| `topup.succeeded` | A top-up was credited. `data` has `amount_usd`, `balance_usd`, `invoice_number`, `source` (`api`, `auto_recharge` or `top_up`), `topup_id` and `idempotency_key`. |
| `topup.failed` | A top-up was not charged. `data` adds `reason` and `message`. |
| `payment.action_required` | A payment, including a top-up by API, waits for a person to confirm it. |
| `balance.low` | The balance is running low. `data` includes `burn_usd_per_hour` and `runway_hours`. |

## MCP, CLI and SDKs

**MCP**

| To | Tool |
| - | - |
| Read the balance and runway | `billing_balance` |
| Top up | `top_up` |
| Read auto-recharge | `get_auto_recharge` |
| Change auto-recharge | `set_auto_recharge` |

The MCP server has no tool for payment methods.

**CLI**

| To | Command |
| - | - |
| Read the balance and runway | `runcrate billing balance` |
| Top up | `runcrate billing topup <amount>` |
| Read auto-recharge | `runcrate billing auto-recharge` |
| Turn auto-recharge on | `runcrate billing auto-recharge set --threshold X --target Y` |
| Turn auto-recharge off | `runcrate billing auto-recharge off` |
| List payment methods | `runcrate billing payment-methods` |

**TypeScript**

```typescript wrap theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
// Read the balance and runway
await rc.billing.getBalance();

// Top up
await rc.billing.topUp({ amountUsd: 50 });

// Read and change auto-recharge
await rc.billing.getAutoRecharge();
await rc.billing.setAutoRecharge({ enabled: true, thresholdUsd: 25, targetUsd: 100 });

// List payment methods
await rc.billing.listPaymentMethods();
```

**Python**

```python wrap theme={"theme":{"light":"github-light","dark":"catppuccin-mocha"}}
# Read the balance and runway
client.billing.get_balance()

# Top up
client.billing.top_up(amount_usd=50)

# Read and change auto-recharge
client.billing.get_auto_recharge()
client.billing.set_auto_recharge(
    enabled=True,
    threshold_usd=25,
    target_usd=100,
)

# List payment methods
client.billing.list_payment_methods()
```

The idempotency key, when you give none:

* The CLI (`--idempotency-key`) and the SDKs (`idempotencyKey`, `idempotency_key`) make a UUID and return it with the answer, so you can re-send it to read the top-up.
* The MCP server derives it from the tool call, so a call the agent's client retries never charges twice.

None of them retries a top-up with a new key on its own.


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