# FluxA Balance Guide

How an AI agent manages the user's AgentMarket Balance. Read this once, it applies to every LLM merchant on the platform. `<merchant>` below stands for a merchant slug from the catalog.

## What the Balance Is

- **$1 buys 1 Balance.** The Balance is what model calls are charged against; you top it up by card, with the FluxA Wallet, or with USDC. Every amount the API returns is in Balance, with up to 5 decimals. See https://agentmarket.fluxapay.xyz/currency.md
- **One Balance per account**, spendable at any provider: Balance bought once is spent wherever the model is served. Users top up in small bundles deliberately, so each charge-up is a known, controlled spend.
- The balance is **signed**: positive = prepaid Balance; negative = unsettled debt, reported as `owed` (the proxy meters into debt until a threshold, then requires settlement via 402).

## Credential Setup

Wallet queries and topups authenticate with one of:

- **API key** — `Authorization: Bearer fxa_live_<key>`. The user creates this at https://agentmarket.fluxapay.xyz/keys (shown once) and gives it to you. This is the user's control handle: which agent holds a key decides which agent can touch the wallet.
- **Agent VC** — `Authorization: Bearer <agent_vc>`. Short-lived (5 min) credential issued by FluxA AgentID; resolves to the wallet owner. No static secret needed. With a VC you can also mint and rotate the user's API keys yourself — see [Managing API Keys](#managing-api-keys).

Per-key limits apply to metered usage only. Topups are controlled by the x402 mandate budget and the confirm-every-topup rule, and read-only queries are never gated.

### Minting an Agent VC

```bash
# (a) Refresh your AgentID JWT if expired
AGENT_JWT=$(curl -sS https://agentid.fluxapay.xyz/refresh \
  -H "Content-Type: application/json" \
  -d '{"agent_id":"<your_agent_id>","token":"<long_lived_token>"}' \
  | jq -r .jwt)

# (b) Mint a fresh Agent VC (one per session, or per call)
AGENT_VC=$(curl -sS https://agentid.fluxapay.xyz/agent/vc/issue \
  -H "Authorization: Bearer $AGENT_JWT" \
  -H "Content-Type: application/json" \
  -d '{"challenge":"wallet-user-info-lookup","ttl_seconds":300,"audience":"fluxa-wallet-service"}' \
  | jq -r .vc)
```

The VC's `sub` claim (`agent_id`) is logged on every billable call for per-agent attribution; the wallet owner's balance is debited, the same pool the human's API key would charge. VCs expire after 5 minutes: mint a fresh one if a multi-step flow (like topup initiate then finalize) takes longer.

## Operations

### 1. Balance

```bash
curl -H "Authorization: Bearer <credential>" https://router.fluxapay.xyz/llm/wallet/balances
```

Returns `balance: { available, spent, burnPerDay, owed }`, all in Balance. One Balance covers every provider. If `balance.available` is 0 or less, offer the topup flow.

### 2. Spending history

```bash
curl -H "Authorization: Bearer <credential>" "https://router.fluxapay.xyz/llm/wallet/ledger/<merchant>?kind=spend&limit=20&offset=0"
```

`kind=spend` shows usage (metering/settlements), `kind=purchases` shows topups, `kind=all` shows everything. Newest first. The reply has `total`, `offset` and `limit` (at most 100); page with `offset` until `offset + entries.length >= total`. Entries carry `id`, `ts`, `type`, signed `change` and `balanceRemaining` (both in Balance), and metadata (model, tokens).

### 3. Pricing

```bash
curl "https://agentmarket.fluxapay.xyz/api/llm/models?vendor=<merchant>"
```

Public, no auth. `rates` are dollars per 1M tokens, drawn from the Balance.

### 4. Bundles on offer

```bash
curl "https://agentmarket.fluxapay.xyz/api/llm/packages?vendor=<merchant>"
```

Public, no auth. Returns the bundle tiers: `cost_credits` (price in dollars) and `balance_granted`.

### 5. Per-merchant docs

```bash
curl https://agentmarket.fluxapay.xyz/marketplace/models/<merchant>/skills.md
```

### 6. Topup

**HARD RULE: confirm every topup with the user.** Even if a signed mandate has remaining budget. Present the bundle menu (from operation 4) with prices in dollars, and get an explicit choice before initiating. Each charge-up must be a known, deliberate spend.

```bash
# 1. After user confirmation (vendorSlug is optional: there is one Balance
#    per account, so naming a merchant only affects bookkeeping):
curl -X POST -H "Authorization: Bearer <credential>" -H "Content-Type: application/json" \
  -d '{"packageSlug":"<bundle-slug>"}' \
  https://router.fluxapay.xyz/llm/topup/initiate
# → HTTP 402 with an accepts array; orderId is a top-level field in the 402 body (also embedded in the resource URL)

# 2. Pay the 402 with the FluxA Wallet CLI (mandate mechanics: see https://agentmarket.fluxapay.xyz/marketplace/apis/skills.md):
fluxa-wallet x402-v3 --mandate <MANDATE_ID> --payload '$PAYLOAD_402'

# 3. Finalize with the payment token:
curl -X POST -H "X-Payment: <xPaymentB64>" https://router.fluxapay.xyz/llm/topup/finalize/<orderId>
# → { success, replayed, added, balanceRemaining } (in Balance). Replays of a settled order are idempotent. If settlement FAILED, the order is dead: a retry returns 409 and you must create a new topup.
```

## Managing API Keys

You can manage the user's `fxa_live_` API keys on their behalf — list, create, update the limit, and revoke. If the user has switched off "Allow agent to obtain API Key directly through CLI" (on https://agentmarket.fluxapay.xyz/keys), creating one needs their approval in the browser, and the key never passes through your output.

**These endpoints require an Agent VC. An `fxa_live_` key cannot manage keys** (a leaked metered key must never be able to mint sibling keys with no limit or revoke other keys). All operations are scoped to the VC owner: you can only see and touch that user's own keys.

### List keys

```bash
curl -H "Authorization: Bearer <agent_vc>" https://router.fluxapay.xyz/llm/keys
```

Returns `keys: [{ id, name, keyPrefix, spendLimit, spent, lastUsedAt, revokedAt, createdAt }]`, with `spendLimit` and `spent` in Balance. Only the prefix is shown — the raw key is never retrievable after creation.

### Create a key

Use the CLI: `fluxa-wallet market keys create --name ci-runner --limit 5`. By default the key is created at once and printed once. If the user has switched off direct keys on /keys, the CLI instead prints a link and a code, opens the browser and waits; the user signs in, checks the code and presses Approve; the key is shown to the user in the browser and the CLI (0.5.8+) saves it to a file and prints only the path. In that mode **never print, read or paste the key into the conversation**; tell the user where it is saved.

```bash
curl -X POST -H "Authorization: Bearer <agent_vc>" -H "Content-Type: application/json" \
  -d '{"name":"ci-runner","spendLimit":5,"publicKey":"<RSA SPKI PEM>"}' https://router.fluxapay.xyz/llm/keys
# → 201 { id, name, keyPrefix, spendLimit, rawKey }   (default)
# → 202 { status: "approval_required", requestId, code, approvalUrl, expiresAt }   (direct keys switched off)
curl -H "Authorization: Bearer <agent_vc>" https://router.fluxapay.xyz/llm/keys/requests/<requestId>
# → { status: "pending" | "approved" | "denied" | "expired" | "claimed", key?: { id, name, keyPrefix, spendLimit }, encryptedKey? }
```

With direct keys switched off, no key is returned by `POST`; `rawKey` (default mode) is returned **once**. `encryptedKey` (RSA-OAEP SHA-256, to `publicKey`) is sent **once**, on the first poll after approval. The link is valid 10 minutes. `spendLimit` is the key's limit, in Balance ($1 = 1 Balance, so `5` is $5). It is optional; omit it or set `<= 0` for no limit. The user can change name and limit before approving. It limits metered LLM usage on that key only. It is a control, not a balance: the account's Balance still has to cover every call.

### Update a key

```bash
curl -X PATCH -H "Authorization: Bearer <agent_vc>" -H "Content-Type: application/json" \
  -d '{"name":"renamed","spendLimit":10}' https://router.fluxapay.xyz/llm/keys/<id>
```

Sets only the fields you send. `spendLimit: 0` (or `null`) clears the limit. Does not reveal or change the raw key.

### Revoke a key

```bash
curl -X DELETE -H "Authorization: Bearer <agent_vc>" https://router.fluxapay.xyz/llm/keys/<id>
# → { success: true }
```

Revocation is immediate and irreversible; the key stops authenticating on its next call. To rotate, create the new key first, hand it off, then revoke the old one — never leave the user with zero working keys mid-flow.

## Reading Balances Honestly

A negative `available` is unsettled debt; the same number is reported positively as `owed`. They are one fact, not two problems. Tell the user plainly: "you owe N Balance, settled on your next topup." Do not hide it.

## Errors

| Status | Meaning | Action |
|--------|---------|--------|
| 401 with hint | Bad or missing credential | Follow the hint: create/check the API key, or mint a fresh Agent VC |
| 503 | Wallet auth service temporarily unavailable | Retry shortly. Do not mint a new VC |
| 404 | Unknown merchant slug | Check the catalog: `curl "https://agentmarket.fluxapay.xyz/api/discover?type=model"` |
| 200 with `neverToppedUp` | No account at this merchant yet | Offer the topup flow |
| 402 on topup initiate | Expected — this is the payment request | Continue the topup flow |

---

- Platform overview: https://agentmarket.fluxapay.xyz/skill.md
- x402 payment mechanics: https://agentmarket.fluxapay.xyz/marketplace/apis/skills.md
- Model catalog: `curl "https://agentmarket.fluxapay.xyz/api/discover?type=model"`