Balance and top-ups

Card subscriptions renew themselves; a one-off crypto payment never does. The prepaid balance is what closes that gap: top it up in crypto and it pays for each connected account as its month comes due, on its own date. These endpoints do that headlessly, with no hosted widget, so your own tooling can quote a top-up, get a deposit address and watch for the credit.

The prepaid balance, headless. Top up in crypto and the balance pays for account seats (€12 per connected account per month) and balance-funded private servers as they come due, which is what makes a crypto customer auto-renewing without anyone being asked to pay again. Minimum €10 per top-up, maximum €500, and the balance itself is capped at €1,000. Credit does not expire. Reading the balance works with a read-only key; creating a top-up needs the full key.

GET/v1/wallet

Balance, anything asked for but not yet landed, and what the balance owes over the next 30 days. Seats and private servers are projected together because they share the one balance, and looking at either alone would call a doomed month healthy. Only occupied seats appear: an empty paid slot is released unbilled when its date passes, so it is not money owed.

200 response
{
  "balance_eur_cents": 2400,
  "pending_topups": [],
  "upcoming": [
    { "when": "2026-10-01T09:12:00Z", "amount_eur_cents": 1200,
      "kind": "seat", "label": "VTMarkets-Demo 1234567" },
    { "when": "2026-10-15T09:12:00Z", "amount_eur_cents": 1200,
      "kind": "seat", "label": "ICMarkets-Live 7654321" }
  ],
  "upcoming_total_eur_cents": 2400,
  "shortfall_eur_cents": 0,
  "covers_upcoming": true
}
GET/v1/wallet/assets

What a top-up of this size can be paid in, one row per coin-and-chain pair, each with the asset amount that size currently costs. Call it before creating a top-up and pass a pair back verbatim: which assets are offered, and how much of each, both depend on the amount. Prefer an is_stable pair for anything but an immediate payment, since a stablecoin quote barely moves inside the 30-minute window and a volatile one can.

  • amount_eur_cents*: The top-up size, in EUR cents. Out-of-bounds amounts are a 400.
200 response
[
  {
    "asset_code": "USDC", "asset_name": "USD Coin",
    "blockchain_code": "ETH", "blockchain_name": "Ethereum",
    "is_evm": true, "is_stable": true,
    "amount": "27.42", "decimals": 6, "display_decimals": 2
  }
]
POST/v1/wallet/topup

Create a top-up and get the address to send to plus exactly how much of the asset to send. Take asset_code and blockchain_code from GET /v1/wallet/assets for the same amount; send only that asset, on that chain, to that address. Under-paying credits only what arrives, because balance is stored value rather than a fixed purchase; over-paying credits the extra, and paying in several transactions works, each credited as it confirms.

  • amount_eur_cents*: €10 to €500, in cents.
  • asset_code*: From GET /v1/wallet/assets, e.g. USDC.
  • blockchain_code*: From the same row, e.g. ETH.
  • is_evm: Only for Web3 / wallet-connect flows. Leave false to be handed a plain deposit address.
Request body
{
  "amount_eur_cents": 2500,
  "asset_code": "USDC",
  "blockchain_code": "ETH",
  "is_evm": false
}
201 response
{
  "id": 4417,
  "status": "pending",
  "amount_eur_cents": 2500,
  "credited_eur_cents": 0,
  "deposit_address": "0x9f3a...c4e5",
  "deposit_amount": "27420000",
  "deposit_amount_decimal": "27.42",
  "asset_code": "USDC",
  "blockchain_code": "ETH",
  "expires_at": "2026-09-11T14:42:00Z"
}
GET/v1/wallet/topup/{id}

Whether the deposit has landed. status is pending, partial, paid or failed, and credited_eur_cents is what has actually arrived, which sits below amount_eur_cents while a payment is short. A pending order reserves nothing: once expires_at passes it is marked failed and the invoice is cancelled at the provider, so the address stops being payable against a quote we no longer honour. Coin that arrives after that is still credited for whatever it was worth.

200 response
{
  "id": 4417,
  "status": "paid",
  "amount_eur_cents": 2500,
  "credited_eur_cents": 2500,
  "deposit_address": "0x9f3a...c4e5",
  "deposit_amount": "27420000",
  "deposit_amount_decimal": "27.42",
  "asset_code": "USDC",
  "blockchain_code": "ETH",
  "expires_at": "2026-09-11T14:42:00Z"
}
GET/v1/subscription

Plan and status, how many account seats are paid for and how many are in use, when the current period ends, and the balance with whatever it owes next. Seats do not share one renewal date: each renews a calendar month after it was bought, so seat_details lists them individually, and account_id is null on a slot that is paid for but not currently in use. wallet is reported whether or not the plan itself is balance-funded, since a card customer can still hold credit, and is null only where the wallet is switched off; next_charge_eur_cents is what the BALANCE owes and is null when it owes nothing.

200 response
{
  "plan": "per_account",
  "status": "active",
  "paid_access": true,
  "billing_provider": "balance",
  "cancel_at_period_end": false,
  "seats": { "total": 2, "used": 2, "bonus": 0 },
  "seat_details": [
    { "id": "4d1e...", "paid_until": "2026-10-01T09:12:00Z",
      "status": "active", "account_id": "6fcb1e23-..." },
    { "id": "8b02...", "paid_until": "2026-10-15T09:12:00Z",
      "status": "active", "account_id": "a91b0d47-..." }
  ],
  "current_period_end": "2026-10-15T09:12:00Z",
  "grace_ends_at": null,
  "wallet": {
    "balance_eur_cents": 2400,
    "next_charge_eur_cents": 1200,
    "next_charge_at": "2026-10-01T09:12:00Z",
    "covers_next_charge": true
  }
}

The balance moves when the signed postback confirms the transaction, not when POST /v1/wallet/topup returns. Poll the top-up or GET /v1/wallet rather than treating the 201 as payment.