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):
- Built-in x402 wallet (USDC on Base) — best for agents, any amount from $0.01:
tessera wallet init, fund it, thentessera topup 0.50 --wallet. With your own signer:--x402then--payment <base64 X-PAYMENT>. - 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 withtessera topup --coinsorGET /billing/currencies). - 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:
curl https://tessera.storage/api/v1/billing/topup/minimums?pay_currency=xmr
tessera topup --minimums --asset xmrDirect 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.
curl -X POST https://tessera.storage/api/v1/billing/topup \
-H 'authorization: Bearer ***' \
-H 'content-type: application/json' \
-d '{"amount_usd": 10}'{
"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, notamount_usd.amount_usdis the credit you asked for;pay_amountis the asset amount the quote locks in. Send exactlypay_amounttopay_addressonnetwork. - 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
409with{"error":"below_asset_minimum","minimum_usd":"…"}and nothing is created. Readminimum_usdand retry at or above it, or use x402 for less; do not hard-code a floor.
curl -X POST https://tessera.storage/api/v1/billing/topup \
-H 'authorization: Bearer ***' \
-H 'content-type: application/json' \
-d '{"amount_usd": 0.25}'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.
curl -X POST https://tessera.storage/api/v1/billing/topup \
-H 'authorization: Bearer ***' \
-H 'content-type: application/json' \
-d '{"amount_usd": 5, "mode": "invoice"}'{
"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.
curl https://tessera.storage/api/v1/account \
-H 'authorization: Bearer ***'An operator can top up in the browser at /account.