> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chipipay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Billing & Credits

> How Chipi credits work — what consumes them, how to top up, and where to monitor balance, holds, and the ledger.

Chipi runs on a **prepaid credit balance** that lives at the org level. You top up by sending USDC to a payment address; service calls (bill payments, gift cards, AI API) deduct credits as they happen. Everything is visible from the **Billing** tab in the dashboard.

<Note>
  Billing is managed entirely from the dashboard at `/configure/billing` — there is no SDK surface to call `/treasury/*` from your app. Credit consumption is **automatic**: when your code calls `purchaseSku`, redeems a gift card, or hits the AI API, the matching debit lands in your ledger server-side. This page explains the model so you know what you're being charged for and how to keep the balance topped up.
</Note>

## The credit model

* **1 credit = 1 USD.** Internally tracked as USDC on Starknet at \$1 parity.
* Each org has one **OrgBalance** with two numbers: `totalUsd` (gross deposits) and `availableUsd` (deposits − pending holds − consumed).
  Each Chipi service that draws on prepaid credits writes one or more `LedgerEntryType` rows so you can audit exactly what was consumed when. Gift cards span three entries (the hold / redeem / release lifecycle); other services map to a single dedicated entry.

| Service (`ServiceType`) | `LedgerEntryType`                                              | Triggered by                                              |
| ----------------------- | -------------------------------------------------------------- | --------------------------------------------------------- |
| `SKU_PURCHASE`          | `SKU_MARGIN`                                                   | `purchaseSku()` settles successfully                      |
| `GIFT_CARD`             | `GIFT_HOLD` → `GIFT_REDEEM` (or `GIFT_HOLD_RELEASE` on cancel) | Gift creation reserves credits; redeem / cancel finalises |
| `AI_API`                | `AI_API_CALL`                                                  | Each `/v1/ai/chat`, `/think`, `/execute` call             |
| `GAS_SUBSIDY`           | `GAS_SUBSIDY`                                                  | Paymaster covers a tx your wallet didn't pay for          |

### Services with a different billing model

Two shipped services don't draw down `availableUsd` from your prepaid credits — they settle differently:

* **x402 protocol payments** ([`@chipi-stack/x402`](https://www.npmjs.com/package/@chipi-stack/x402), facilitator at [x402.chipipay.com](https://x402.chipipay.com)) — settles **per call** via the x402 protocol on Starknet. The end user pays the merchant directly through the facilitator; nothing flows through `OrgBalance`. See the **x402 Payments** dropdown for the full integration guide. The `ServiceType.X402` enum value is reserved for any future flows that *do* route through credits (e.g., facilitator fees), but no runtime code uses it today.
* **Pay-with-crypto button** (`@chipi-stack/backend` + a UI component shipped in the dashboard) — a checkout primitive that sends USDC straight to the merchant wallet. Same model: direct on-chain settlement, no credit debit. The `ServiceType.PAY_BUTTON` enum value is reserved.

`ServiceType.KYC_VERIFICATION` and `ServiceType.ADDRESS_SCREENING` are reserved for compliance services not yet shipped. When they go live, the service entry above will be updated.

Top-ups land as `DEPOSIT`. Adjustments (manual reconciliation), `REFUND`, `REV_SHARE_CREDIT/PAYOUT`, `SETTLEMENT` (catch-all), and `WITHDRAWAL` round out the `LedgerEntryType` vocabulary.

## Credit packs

There are five purchasable packs. Each ships with a number of free gift-card credits as a bundled bonus.

| Pack       | Amount   | Free gift cards | Tagline          |
| ---------- | -------- | --------------- | ---------------- |
| Tester     | \$2      | 1               | Learn first      |
| Starter    | \$5      | 2               | Try it out       |
| **Growth** | **\$25** | **10**          | **Most popular** |
| Scale      | \$50     | 25              |                  |
| Pro        | \$100    | 50              |                  |

The packs are returned by `GET /treasury/packs` and rendered in the dashboard's Billing tab.

## How top-up works

```
┌──────────────┐  1. register address  ┌────────────────────┐
│  Your wallet │ ─────────────────────▶│ POST /treasury/    │
│  (Starknet)  │                       │   register-sender  │
└──────────────┘                       └────────────────────┘
        │
        │  2. send USDC
        ▼
┌────────────────────────────────────┐
│  Chipi credits address             │
│  (CHIPI_CREDITS_ADDRESS env var)   │
└────────────────────────────────────┘
        │
        │  3. background detector matches deposit
        │     to a registered sender → org
        ▼
┌────────────────────────────────────┐
│  OrgBalance += deposit             │
│  LedgerEntry { type: DEPOSIT }     │
└────────────────────────────────────┘
```

1. **Register your sender wallet** so the deposit detector knows which org gets credit. The dashboard's Billing tab does this when you select a credit pack — it `POST`s to `/treasury/register-sender` with your Starknet address.
2. **Send USDC** from that address to the Chipi credits address (revealed in the dashboard after registration).
3. **Wait \~2 minutes.** The detector runs every 2 min on Trigger.dev. The dashboard polls for up to 3 min after a purchase before timing out the optimistic UI; the actual credit lands as soon as the next detector cycle catches it.

You can re-register the same address (it's an upsert), and one org can have multiple registered senders.

## Holds — the gift card pattern

Some services use a **hold-then-consume** pattern instead of a flat debit. It protects against partial failures and over-consumption when an action takes time to settle:

1. **Hold** — when the action starts, Chipi reserves an estimated max cost (`POST /treasury/hold`). The held amount drops `availableUsd` immediately so concurrent calls see the correct headroom. Lands in the ledger as `GIFT_HOLD`.
2. **Consume** — when the action finishes, the actual cost gets `consume-hold`'d. Only the actual amount is debited; any unused reservation is freed. Lands as `GIFT_REDEEM`.
3. **Release** — if the action errors out before completing, the entire hold is `release-hold`'d (full refund, no debit). Lands as `GIFT_HOLD_RELEASE`.

**Currently only Gifts use this pattern.** Bill payments use a flat `SKU_MARGIN` debit at settlement; AI API uses an up-front `checkBalance` + a final `AI_API_CALL` debit (no hold lifecycle, no separate ledger lines for in-flight calls). The hold / consume / release primitives are general-purpose and would suit any future long-running flow (e.g., streaming AI calls), but no other service uses them today.

You don't call `/treasury/hold` directly — the service that needs it does so server-side.

## Monitoring

In the dashboard at **`/configure/billing`** you get:

* **Balance card** — `totalUsd` deposited, `availableUsd` after holds/debits, status (`OK` / `LOW` / `CRITICAL` / `SUSPENDED`).
* **Ledger table** — paginated `GET /treasury/ledger`, filterable by `type` (any `LedgerEntryType`) and date range.
* **Wallet address** — your treasury wallet, one per org. Created on demand by `POST /treasury/create-wallet`.
* **Credit packs** — the catalog above plus a purchase modal that handles registration + payment-address reveal.

You can configure two thresholds via `PATCH /treasury/settings`:

* `lowBalanceThresholdUsd` — when `availableUsd` drops below this, the balance card shows `LOW`. Wire to a webhook for alerting (coming soon).
* `criticalThresholdUsd` — same but at `CRITICAL`. When you hit zero, the org is `SUSPENDED` and service calls reject with a credit-insufficient error until you top up.

## What this means for your code

**Devs writing app code** don't call `/treasury/*`. Instead:

* Sufficient credits → service calls succeed and the debit lands silently in the ledger.
* Insufficient credits → calls reject with a credit-insufficient error (you'll see it as a `ChipiApiError` from the SDK; check `.code` for the specific reason).

So the right pattern in your backend is to **catch the error, surface it to the user, and prompt them (or you) to top up** — not to pre-check `/treasury/balance` before every call. See [Error Handling](/services/gasless/errors) for the catch patterns.

## Where each thing lives

| Capability         | Dashboard URL                    | Endpoint                                                    |
| ------------------ | -------------------------------- | ----------------------------------------------------------- |
| Balance + status   | `/configure/billing`             | `GET /treasury/balance`                                     |
| Ledger / activity  | `/configure/billing`             | `GET /treasury/ledger`                                      |
| Buy credit pack    | `/configure/billing` (Buy modal) | `GET /treasury/packs` + `POST /treasury/register-sender`    |
| Treasury wallet    | `/configure/billing`             | `GET /treasury/wallet-info`, `POST /treasury/create-wallet` |
| Threshold settings | `/configure/billing`             | `PATCH /treasury/settings`                                  |

> ✅ Endpoint contracts, credit pack catalog, and `LedgerEntryType` / `ServiceType` enums verified against the live `/treasury/*` API on **2026-05-10**.
