Adding funds.

The technical validation on the main agent page needs no payment. This page is for after it — continued use is pay-as-you-go against a prepaid USD balance.

There are three ways to pay (tessera topup --help explains them too):

  1. Built-in x402 wallet (USDC on Base) — best for agents, any amount from $0.01: tessera wallet init, fund it, then tessera topup 0.50 --wallet. With your own signer: --x402 then --payment <base64 X-PAYMENT>.
  2. Deposit address in a coin you choose — USDC on Base by default (tessera topup 10) or any accepted coin: tessera topup 10 --asset xmr (btc, eth, sol, ltc, usdttrc20, usdtbsc… — list them with tessera topup --coins or GET /billing/currencies).
  3. Browser invoice — tessera topup 1 --invoice; pick any accepted coin (USDC, USDT, BTC, ETH, XMR, SOL, LTC and hundreds more) on the page.

Methods 2 and 3 carry the payment processor's per-coin minimum (about $0.38 for USDC on Base, about $7 for Monero, about $12 for USDT on Tron on 2026-10-30); method 1 has none beyond Tessera's $0.01 floor. Read the live values first:

shell
curl https://tessera.storage/api/v1/billing/topup/minimums?pay_currency=xmr
tessera topup --minimums --asset xmr

Direct pay-by-address (default)

Ask for the USD amount you want credited. The API returns a deposit address and the exact asset amount to send.

shell
curl -X POST https://tessera.storage/api/v1/billing/topup \
  -H 'authorization: Bearer ***' \
  -H 'content-type: application/json' \
  -d '{"amount_usd": 10}'
json
{
  "payment_intent_id": 42,
  "invoice_url": "https://…",
  "expires_at": "2026-10-29T16:49:09Z",
  "status": "pending",
  "direct_payment": true,
  "pay_address": "0x…",
  "pay_amount": 10.201235,
  "pay_currency": "usdcbase",
  "network": "base",
  "address_expires_at": "2026-10-28T16:59:10Z"
}
  • Send pay_amount, not amount_usd.amount_usd is the credit you asked for; pay_amount is the asset amount the quote locks in. Send exactly pay_amount to pay_address on network.
  • The address is short-lived. Its rate quote lasts about 20 minutes (address_expires_at, UTC); the invoice link lasts 24 hours (expires_at). The CLI shows both in your local time with the zone named. If the address window lapses before you send, request a new address or pay through the invoice link.
  • The quote is floating-rate and the merchant absorbs the fee. A completed $10 top-up credits the full $10 requested, even if the processor fee reduces the net amount received. A partial payment credits the fraction actually paid. The exact credited amount is recorded after the payment confirms.
  • The minimum moves with the rate. It is roughly $0.38 in USDC on Base. Below it — for a direct address or an invoice — the API answers 409 with {"error":"below_asset_minimum","minimum_usd":"…"} and nothing is created. Read minimum_usd and retry at or above it, or use x402 for less; do not hard-code a floor.
shell
curl -X POST https://tessera.storage/api/v1/billing/topup \
  -H 'authorization: Bearer ***' \
  -H 'content-type: application/json' \
  -d '{"amount_usd": 0.25}'
response
409 Conflict

{
  "error": "below_asset_minimum",
  "minimum_usd": "0.38",
  "rail": "nowpayments",
  "mode": "direct",
  "pay_currency": "usdcbase",
  "asset": "USDC on Base",
  "message": "The minimum crypto top-up in USDC on Base is $0.38 (set by the payment processor). Enter at least that amount. Smaller amounts (down to the $0.01 floor) work only with x402 (USDC on Base), e.g. the CLI's built-in wallet: tessera wallet init, then tessera topup <usd> --wallet."
}

If direct_payment is false, no address was issued; the response still carries invoice_url, so use the hosted page.

Hosted invoice (alternative). The hosted page remains available and lets the payer choose the coin there, including that coin's own minimum. It is checked against the cheapest coin's minimum, so an invoice nobody could pay is never created. Pass "mode": "invoice" (or run tessera topup 1 --invoice). The response is the invoice link; the balance credits after the payment confirms.

shell
curl -X POST https://tessera.storage/api/v1/billing/topup \
  -H 'authorization: Bearer ***' \
  -H 'content-type: application/json' \
  -d '{"amount_usd": 5, "mode": "invoice"}'
json
{
  "payment_intent_id": 43,
  "invoice_url": "https://…",
  "expires_at": "2026-10-29T16:49:10Z"
}

Either path returns a payment_intent_id. Poll GET /billing/topup/{id} (or pass --wait to the CLI) for status and credited_usd. Status is pending, partial, completed, expired or failed.

x402 (USDC on Base mainnet)

x402 pays the same balance with a signed USDC authorization on Base mainnet. Availability is per account: read the topup_rails object from GET /account (the CLI prints it in tessera account --json) and check topup_rails.x402. The top-level topup_open is the master switch and can be false while a named rail is open, so use the per-rail field. When x402 is true, POST /billing/topup accepts {"amount_usd": 10, "rail": "x402"} and returns the 402 challenge to sign; when it is false, the same request answers 503 topup_closed, and the direct or invoice path above is the one to use. Branch on the field rather than assuming either state. Signing details are in Accounts.

shell
curl https://tessera.storage/api/v1/account \
  -H 'authorization: Bearer ***'

An operator can top up in the browser at /account.