# Core concepts

## Environments

| Environment | Base URL | Network | Money |
| --- | --- | --- | --- |
| Production | `https://partner.packflip.xyz` | Base | Real USDC and real cards |
| Sandbox | `https://sandbox.partner.packflip.xyz` | Base Sepolia | Test credit and a synthetic catalog |

Both environments run the same API and share one console login, so an organization exists in both. Everything else is separate: API keys, balances, customers, cards, and webhook endpoints. A sandbox key does not work in production, and the reverse.

## Account and organization

Your Partner account is an organization in the console. Every API key acts for the whole organization, and every request is scoped to it. There is no partner ID in URLs: the key identifies the account.

`GET /api/v2/account` returns the account, including its public ID (`par_…`).

## Balance and ledger

Operations are paid from a prepaid USD balance with two buckets:

- **cash**: value you funded, plus buyback credits.
- **bonus**: promotional value granted by Packflip. It is spent before cash.

A refund returns value to the bucket it was spent from.

`GET /api/v2/balance` returns `cash`, `bonus`, and `total`. Every change is an immutable entry in the ledger (`GET /api/v2/ledger-entries`), with a `kind` of `TOPUP`, `BONUS`, `BUYBACK`, `SPEND`, or `ADJUSTMENT`.

All money values are decimal strings with six places, such as `"25.000000"`. Parse them with a decimal type, never as floating-point numbers.

## Catalog

A vending machine (`GET /api/v2/vending-machines`) is a pack with a fixed `priceUsd`. Each draw selects a card at random according to the machine's published odds and stock.

Each machine has three image fields:

- `image`: the pack artwork, possibly animated. Show this one.
- `imageSrcset`: the same artwork at 256, 512, and 1024 pixels wide, for `srcset`. `null` when resizing is unavailable.
- `staticImage`: a still frame for first paint, for example as a placeholder while `image` loads or where animation is unwanted. It can be an empty string; fall back to `image`.

## Customers

A customer (`cus_…`) is one of your end users as Packflip sees them: a pseudonymous identity that cards and operations attach to. Packflip uses customers to understand purchase behaviour across packs, and they will power the reports you see in the console.

- `externalUserId` is optional. Set it to your own user ID when your customers map to users, then find a customer with `GET /api/v2/customers/by-external-id/{externalUserId}`. Once set, it cannot be moved to another customer. Leave it out when that does not fit your product, for example when cards go to randomly chosen winners.
- `attributes` is an optional JSON object of analytics properties, such as `{ "plan": "pro", "country": "JP", "channel": "campaign-2026-09" }`. Updates merge into the stored object, and a key set to `null` is removed. Limits: 50 keys, values nested at most two levels, 8 KB in total, and keys may not start with `$`.

Customers hold no contact details and no wallets. Shipping details are sent with each redemption, and the destination wallet with each mint. Do not put names, email addresses, or other directly identifying data in `attributes`.

```bash
curl -X PATCH "$PACKFLIP_BASE_URL/api/v2/customers/cus_…" \
  -H "Authorization: Bearer $PACKFLIP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "attributes": { "plan": "pro", "trial": null } }'
```

## Cards

A card (`card_…`) is a real graded collectible, held by Packflip for the customer until they decide what to do with it.

| Status | Meaning |
| --- | --- |
| `active` | Owned by the customer: held by Packflip, or minted to their wallet. |
| `reserved` | Legacy state for cards drawn before they were minted. New integrations do not create it. |
| `buyback_pending` | A minted card waiting for its on-chain burn. |
| `bought_back` | Sold back to Packflip. |
| `redeemed` | Shipped, or being shipped, to the customer. |
| `refunded` | A sealed card that was refunded. |
| `cancelled` | The draw was cancelled and the card returned to stock (legacy draw-and-mint orders only). |

A card also has:

- `sealed`: `true` while the card is hidden. Its `name`, `image`, `buybackUsd`, and `metadata` are `null` until it is revealed.
- `onchain`: set once a mint is authorized. `onchain.mintedAt` stays `null` until the mint transaction confirms.
- `buybackUsd`: the current market buyback value. It changes over time; it is not a quote.

### What you can do with a card

Every action needs `status` to be `active`. The other two fields decide the rest:

| `sealed` | `onchain` | State | Allowed actions |
| --- | --- | --- | --- |
| `true` | `null` | Sealed | Reveal; refund until `refundableUntil` |
| `false` | `null` | Held | Off-chain buyback, redemption, mint |
| `false` | set, `mintedAt` is `null` | Mint pending | Submit or cancel the mint (see [On-chain cards](/docs/onchain)) |
| `false` | set, `mintedAt` is set | Minted | On-chain buyback |

A card is **held**, and eligible for off-chain buyback and redemption, exactly when `status` is `active`, `sealed` is `false`, and `onchain` is `null`. A cancelled or expired mint clears `onchain`, so the card is held again. Any other state returns `409 conflict` for the off-chain actions.

## Operations

Every change to cards or balance is an operation (`op_…`), created with `POST /api/v2/operations`:

| `kind` | `mode` | What it does |
| --- | --- | --- |
| `order` | `offchain` | Draws cards and charges the pack price. |
| `refund` | `offchain` | Refunds sealed cards within their refund window. |
| `buyback` | `offchain` | Buys back held cards and credits their value. |
| `buyback` | `onchain` | Buys back minted cards once their NFTs are burned. |
| `redemption` | `offchain` | Ships held cards to the customer and charges shipping. |
| `mint` | `onchain` | Withdraws held cards as NFTs to the customer's wallet. |

An operation's `status` is `pending`, `awaiting_chain`, `completed`, `failed`, or `cancelled`. Off-chain operations complete within the request. On-chain operations return `awaiting_chain` until the transactions confirm. See [On-chain cards](/docs/onchain).
