Core concepts
Environments, balance, customers, cards, and operations.
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, forsrcset.nullwhen resizing is unavailable.staticImage: a still frame for first paint, for example as a placeholder whileimageloads or where animation is unwanted. It can be an empty string; fall back toimage.
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.
externalUserIdis optional. Set it to your own user ID when your customers map to users, then find a customer withGET /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.attributesis 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 tonullis 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.
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:truewhile the card is hidden. Itsname,image,buybackUsd, andmetadataarenulluntil it is revealed.onchain: set once a mint is authorized.onchain.mintedAtstaysnulluntil 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) |
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.