Skip to content
JuicePay

JuicePay documentation

Create account

Documentation

Accounts and balances

How a single balance spans many chains without you reconciling by hand.

An account is the top-level container for everything you do on JuicePay. It holds balances, keys, policy rules, and the audit log. Most teams need exactly one account per legal entity.

The balance model#

The important idea is that a balance is not per chain. A balance is per asset, and it is backed by holdings spread across any number of chains.

json
{
  "asset": "USDC",
  "available": "312904.180000",
  "pending": "4210.000000",
  "chains": ["base", "solana", "arbitrum", "polygon"]
}

Three figures matter:

FieldMeaning
availableSpendable right now, net of any in-flight outbound legs.
pendingInbound value seen onchain but not yet final.
chainsNetworks currently contributing to this balance.

When you create a payout, JuicePay decides which chain to source it from. It prefers to spend from the chain holding the largest surplus so that consolidation sweeps stay small.

Sub-accounts#

Sub-accounts let you separate funds that share an account but must not be commingled — regional entities, product lines, or client money. Each sub-account has its own balances, keys, and policy rules, while the parent account keeps one consolidated view and one billing relationship.

bash
curl -s -X POST "$JUICEPAY_URL/v1/sub_accounts" \
  -H "Authorization: Bearer $JUICEPAY_KEY" \
  -H "Idempotency-Key: subacct-eu-001" \
  -d '{ "name": "EU Operations", "parent_behavior": "allow_transfers" }'

Setting parent_behavior to isolate prevents the parent from moving funds without an explicit approval, which is usually what auditors want to see for client money.

Receive addresses#

Each sub-account has a permanent address per chain. These do not rotate; share them freely with counterparties. A single onchain deposit is credited to whichever sub-account owns the address.

Addresses are chain-specific. A USDC deposit sent on Base to a Solana address is not recoverable by JuicePay — it is a well-formed transfer to a wallet nobody controls.

To avoid that class of mistake, use payment intents rather than raw addresses whenever a customer is involved. An intent tells the payer which chains are acceptable and rejects everything else.

Sweeps#

Because holdings live on many chains, they drift. Sweeps consolidate value onto your preferred chain on a schedule you control.

json
{
  "schedule": "0 */6 * * *",
  "target_chain": "base",
  "target_asset": "USDC",
  "min_amount": "500.00",
  "exclude_assets": ["EURC"]
}

Sweeps never touch funds reserved for an in-flight payout, and they respect the min_amount floor so a $3 surplus does not trigger a $0.90 transfer.

Ledger entries#

Every movement writes at least two ledger entries. They are immutable and exportable.

bash
curl -s "$JUICEPAY_URL/v1/ledger_entries?sub_account=sa_eu01&limit=2" \
  -H "Authorization: Bearer $JUICEPAY_KEY" | jq
json
{
  "object": "list",
  "data": [
    {
      "id": "le_7hQ2mN",
      "direction": "credit",
      "asset": "USDC",
      "amount": "4250.000000",
      "chain": "base",
      "source": "payment_intent:pi_9fK2xQ",
      "created": "2026-08-19T09:14:02Z"
    },
    {
      "id": "le_7hQ2mP",
      "direction": "debit",
      "asset": "USDC",
      "amount": "0.410000",
      "chain": "base",
      "source": "network_fee:pi_9fK2xQ",
      "created": "2026-08-19T09:14:02Z"
    }
  ]
}

Book against these entries rather than against a bank-style running total. They are the authoritative record of what happened.