Skip to main content
PaymentsPrefunded USDC

Deposited USDC Credits

Deposit USDC once, spend it like credit, and withdraw whatever you did not use. It is the only funded balance on Surplus that can leave again. (Elsewhere in the API and the dashboard this is called prefunded USDC — same thing.)

Card top-ups create credit — an internal ledger balance that can be spent and never withdrawn. Prefunded USDC is the other half: you send USDC to a deposit address owned by your workspace, it becomes a spendable balance, and the unspent remainder can go back to your wallet.

This is live. Prefunded USDC and deposit intake are both enabled in production. If an endpoint below returns 404 rather than 401/403, that is a feature gate rather than a wrong URL — but on production today, it is on.

The distinction that matters

Both balances add up to one spendable number in the dashboard. Underneath they are not interchangeable.

Prefunded USDCDeposited on chain. Spendable, and you can withdraw whatever you do not use.
Card creditBought with a card. Spendable, but it can never leave — so it is spent first.

That is also why the spend order is what it is: card credit is spent first, prefunded USDC second. Spending the non-withdrawable money first preserves the withdrawable money for as long as possible. If the order were reversed, every request would quietly convert money you could get back into money you could not.

Deposit, spend, withdraw
  1. Get a deposit addressGET /v1/payments/deposit-address
  2. Send USDC on BaseFrom any wallet
  3. ConfirmationsCredited once final
  4. Spend or withdrawPOST /v1/payments/withdraw
Deposits need on-chain confirmations before they are spendable. Withdrawals go only to a wallet the account has registered.

Depositing

bash
curl -s $SURPLUS_BASE_URL/payments/deposit-address \
  -H "Authorization: Bearer inf_..."

The address is per workspace and stable — reuse it. Send USDC on Base to it from any wallet. The balance is not spendable the moment the transaction lands: it is credited after the configured minimum confirmations, which exists so a reorg cannot spend money that never settled.

Requires billing.manage.

Withdrawing

bash
curl -s -X POST $SURPLUS_BASE_URL/payments/withdraw \
  -H "Authorization: Bearer inf_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'Content-Type: application/json' \
  -d '{ "destination": "0xYourWallet", "amount_micro": "25000000" }'
  • destinationstring

    Where the USDC goes. Not a free-text field — it must be your own account wallet or a wallet the org has registered. An address that is neither is rejected, so a leaked key cannot drain funds to an attacker.

  • amount_microstring

    Integer microdollars as a string (1 USD = 1000000). Omit to withdraw everything available. A string because a JSON number silently accepts 1.5, and a fractional microdollar is not a representable amount of USDC.

Idempotency-Key is required and becomes the withdrawal's permanent identity, so a retried request can never withdraw twice. Requires wallets.manage — deliberately a different capability from billing.manage, because taking money out is a different decision from putting money in.

For a signed, multi-step withdrawal there is also POST /v1/wallet/withdraw/prepare, POST /v1/wallet/withdraw and GET /v1/wallet/withdraw/:id. Those require a signed-in session rather than an API key.

What you can withdraw

Only the prefunded USDC portion. Card credit is never withdrawable, and a withdrawal request cannot reach it.

If either ledger row is frozen, both are frozen — the balances are one funding decision, so a hold on one side stops spending from the other too. That is intentional, not a bug.

Auto top-up

Keep a floor under the balance rather than watching it:

GETPath · /v1/payments/auto-topupDoes · Read the current rule
PUTPath · /v1/payments/auto-topupDoes · Set a threshold and amount
DELETEPath · /v1/payments/auto-topupDoes · Turn it off

Writes require billing.manage and are idempotent.