---
title: "Create a top-up"
method: POST
path: "/v1/balance/top-ups"
tags: ["Treasury"]
---

# Create a top-up

`POST /v1/balance/top-ups`

## Request

- Base URL: `https://zernio.com/api`
- URL: `https://zernio.com/api/v1/balance/top-ups`
- Auth: HTTP bearer

Charges the billing owner's saved card (the default one, or `paymentMethodId`) for `amountCents` while the payer is present.

- `status: succeeded`: the card was charged. The top-up is credited to `availableCents` within seconds, net of Stripe's processing fee. Transfers to payees can use it from 7 days after the charge.
- `status: requires_action`: the bank asked for 3D Secure. Complete it with Stripe.js `handleNextAction({ clientSecret })`, then call `POST /v1/balance/top-ups/{topupId}/confirm`. One left unconfirmed is cancelled after 30 minutes.

### Errors

- 402 `payment_required`: no saved card.
- 402 `card_declined`: the bank's reason.
- 400: `method: bank_transfer` (bank transfers are not available yet).

Needs an owner, admin or billing admin, signed in or through a full-access key they created.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `amountCents` integer, required: Amount in cents, minimum $10
  - `method` 'card' | 'bank_transfer', required: Only card is available. bank_transfer returns 400 "Bank transfers are not available yet."
  - `paymentMethodId` string: A saved card of the billing owner's (`pm_...`). Defaults to the default payment method.

## Response `201`

The card was charged, or the bank asked for 3D Secure

- object
  - `topupId` string, required
  - `status` 'succeeded' | 'processing' | 'requires_action', required
  - `clientSecret` string, nullable, required: PaymentIntent client secret for Stripe.js handleNextAction; set only when status is requires_action

## Other responses

- `400`: Invalid request
- `402`: No saved card (code `payment_required`), or the bank declined the charge (code `card_declined`, the message is the reason), or a billing gate.
- `403`: Refused (`insufficient_permissions`). One of: - A restricted API key without the `money` resource group. - A profile-scoped API key. Money leaves the whole team's balance, so moving it needs a full-access key or a signed-in session ("Moving money needs a full-access key or a signed-in session."). - A member or viewer, signed in or through a key they created. Only an owner, admin or billing admin moves money.
- `404`: Resource not found
- `409`: Same Idempotency-Key still processing; retry after a short backoff
- `422`: Idempotency-Key reused with a different request

## Changes

- **2026-10-29** `dbfaa35515ac`: 3 breaking, 8 warning, 18 info
  - the response property `code` became optional for the status `402`
  - the response property `error` became optional for the status `402`
  - removed the required property `reason` from the response with the `402` status
  - removed the optional property `dashboard_url` from the response with the `402` status
  - …25 more
- **2026-10-28** `8f2c44dd7550`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/zernio/apis/zernio-api/changes/v1/balance/top-ups/post.md)

---

[API](https://skmtc.dev/zernio/apis/zernio-api.md) - [All operations](https://skmtc.dev/zernio/apis/zernio-api/llms.txt) - [OpenAPI document](https://skmtc.dev/zernio/apis/zernio-api/revisions/c319eb5d95b6?raw)
