Phygitals Partner API
The Phygitals Partner API lets you embed Phygitals' pack, buyback, and physical-shipping flows into your own platform: browse and buy packs, look up the items inside them, sell items back to the pool, and quote, request, and track physical shipments. Everything is REST over HTTPS with JSON request and response bodies. The operations are grouped in the sidebar — Storefront (read + buy) and Events (outbound webhooks).
Platform
The infrastructure for digital collectibles. Sell digital packs backed by real, graded physical
cards. An end user buys a pack, watches it reveal instantly, holds the card in an insured vault, and
then ships it or sells it back whenever they want — all through this one API. These are the same
rails the main Phygitals storefront runs on: $350M+ GMV, 2.8M+ transactions, 100K+ users,
and a pack-configured buyback (often ~85–90% of FMV — see each pack's buyback_percent).
The four-step flow
-
Buy a pack — debited from your prepaid partner ledger (not end-user card/crypto on this
API). Each pack is backed by real physical cards. Start with
POST /vm/buy/init; look it up withPOST /vm/buy/status(or thepurchase.settledwebhook). -
Instant reveal — the picked cards are known the moment the pack opens.
buy/initreturns them synchronously (there is nopendingstate to poll for). -
Securely vaulted — cards sit in top-tier insured US facilities; the user owns the digital
representation until they act on it. Holdings are surfaced by
GET /inventory/{user_id}. -
Ship or sell — redeem for worldwide physical shipping (
POST /ship/quote→POST /ship/request→GET /ship/order/{order_id}), or sell back at the pack'sbuyback_percentof FMV viaPOST /vm/buyback.
Vault, pricing & logistics
Every digital collectible is 1:1 backed by a real, graded card in an insured US vault. Phygitals operates the full physical stack end-to-end — you don't store or fulfill anything yourself.
| Vault partners | Alt (primary), PSA, Fanatics |
| Pricing | Alt's live FMV feed powers buyback_price on every item and every sellback |
| Insurance | Full coverage across all stored items, climate-controlled facilities |
| Fulfillment | Worldwide shipping, tracking and insurance included on every redemption |
Phygitals funds the buybacks. Sellback liquidity is underwritten by Phygitals, not the partner.
When a user calls POST /vm/buyback, the credit hits your partner ledger — you don't post capital,
run a secondary market, or carry sellback risk.
White-label. Phygitals is both a direct-to-consumer platform and infrastructure for partner brands. A launch can be API-only (you keep your own frontend and integrate these endpoints) or a fully managed build (Phygitals designs the branded storefront and backend on the same rails). Every partner gets a dedicated point of contact. Partnership inquiries: hello@phygitals.com.
Concepts
-
Pack — a themed, purchasable bundle (called a claw internally — the id the
claw_idsfilter references). Buying one draws items from it provably-fair and returns them. - Item — an individual card / NFT pulled from a pack. You can look one up, ship it, or sell it back to the pool.
-
Chase card — one of a pack's headline high-value cards, its "top hits": the marquee,
long-odds pulls a pack can yield (a numbered rookie autograph, a low-population parallel, and the
like).
GET /vm/chase/{slug}returns them —id,name, front/backimage, andfmv— so you can render a "what could I pull?" preview before a buyer purchases. They're the best-case outcomes in the pack's pool, not a guarantee that any given buy lands one. (GET /vm/availablealways returnschase: []— use the chase endpoint for top hits.) -
Buyback — selling an item back to the pool at the pack's
buyback_percentof FMV for instant liquidity, viaPOST /vm/buyback. Phygitals funds it; the partner posts no capital. - Vault — the insured US facility (Alt / PSA / Fanatics) holding the physical card behind each item until the user ships or sells it.
- Partner ledger — prepaid balance Phygitals debits on buy/ship and credits on buyback. Live partner settlement is ledger-based, not an on-chain payment from the end user.
Getting an API key
Access is provisioned per partner: you need a Partner account on Phygitals before you can call the API. Once your organization is set up, an owner of your partner mints keys from the partner dashboard (the "Docs & setup" panel):
- Sandbox keys (
pk_sandbox_…) — a partner owner can create these self-serve. Start here. -
Live keys (
pk_live_…) — issued by Phygitals only. A partner owner cannot self-issue one; contact your Phygitals partner manager (or support) to enable production access.
The plaintext secret is shown exactly once, at creation — store it somewhere safe. If you lose it, revoke the key and mint a new one. Don't have a partner account yet? Talk to your Phygitals contact — or email hello@phygitals.com — to get onboarded.
Authentication
Every request must be authenticated. Two credentials are accepted, and either one satisfies a request (it's an OR):
-
X-API-Key: <your-key>— your partner API key. This is the primary credential for server-to-server integrations, and the only way to reach live data. -
Authorization: Bearer <privy-token>— a Privy access token for a user whitelisted to your partner platform. A Privy session always resolves to sandbox; live access requires apk_live_…API key.
curl https://api.phygitals.com/api/v1/vm/available \
-H "X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxx"
Base URLs & environments
Production is https://api.phygitals.com/api/v1 — that's the base URL to build against, and
the one every example here uses. This reference is served on it too: the docs you're reading are at
/api/v1/, and the raw OpenAPI document at /api/v1/spec.json.
The same operations are mounted on four prefixes; pick the one for the environment you want.
| Environment | Base URL | Settlement |
|---|---|---|
| Production (live) | /api/v1 |
Prepaid partner ledger + real vaulted inventory |
| Production (long form) | /api/partner/v1 |
Identical to /api/v1 — same handler, same mode |
| Sandbox | /api/partner/sandbox/v1 |
Fully simulated (no ledger / vault / chain) |
| Legacy | /_/api/v1 |
Follows the key's mode |
Sandbox runs the full API against a simulated backend — buys, buybacks, and settlement are emulated and nothing touches the vault or chain — so you can build and test end-to-end for free. The sandbox base URL forces sandbox behavior for any credential, so even a live key is safely downgraded there.
The production base URL requires a live key: a sandbox-scoped credential (a pk_sandbox_…
key, or a Privy session) is rejected with 403 rather than silently escalated to live data.
/api/partner/v1 is a long-form alias of /api/v1 — the same handler in the same forced live
mode — kept for integrations that already call it; prefer the short form. The legacy /_/api/v1
prefix also keeps working and follows the key's own mode, but new integrations should use /api/v1
(or /api/partner/sandbox/v1 to start in sandbox).
Conventions
- Field names are snake_case in every request and response body.
-
Money types vary by field: pack listing prices / admin pack rows use decimal strings
(e.g.
"25"); itemfmv/buyback_price, buy/buybackamount, and ship rate costs are JSON numbers. Never invent floats where the schema uses a string. Timestamps are ISO-8601 / RFC-3339 UTC strings (e.g."2026-01-15T09:30:00Z"). - Idempotency is supported on every write — see the dedicated section below.
Idempotency
Every mutating request (buy, buyback, create-pack, …) can carry an idempotency key so a dropped connection can't double-execute. Send one on every write.
-
Two equivalent channels. Supply the key as an
Idempotency-Key: <token>HTTP header or as anidempotency_keyfield in the JSON body — they are interchangeable. If you send both, the body field wins and the header is ignored. -
Replay. Retrying with the same key and the same request body replays the original response
verbatim, carrying an
idempotency-replayed: trueresponse header, instead of executing again. Stored responses stay replayable for 24 hours. - Reuse conflict. Reusing a key with a different request body is rejected
422 IDEMPOTENCY_KEY_REUSED— a sign a key was recycled by mistake. -
In-flight duplicate. The same key retried before the first call has returned is rejected
409 DUPLICATE_REQUEST; wait for the original to finish (or poll the status endpoint) rather than retrying immediately. - Failed attempts don't stick. A key whose only prior attempt failed is not blocked: the retry executes fresh, since nothing was charged.
-
Looking up a past attempt.
POST /vm/buy/statusandPOST /vm/buyback/statusaccept the sameidempotency_keyyou sent on the original write to resolve its durable outcome. There the key is a lookup key, not a dedup token, so passing it never trips the reuse conflict above.
Sandbox
The sandbox runs the full API against an in-memory simulation, so you can integrate and test the
whole buy → inventory → sellback → shipping flow without consuming inventory, moving money, or
shipping real cards. Point at /api/partner/sandbox/v1 (or use a pk_sandbox_… key). It mirrors the
live surface — same routes, same request/response shapes, same error codes — but a few behaviors
differ, and you should know them before you rely on them:
- State is in-memory and ephemeral. Sandbox sessions, simulated inventory, buybacks, and shipping orders live in the API process's memory and are wiped on restart or deploy; separate instances don't share them. Treat every sandbox run as fresh.
-
No real fulfillment. Nothing touches the vault (Alt / PSA / Fanatics), the chain, a payment
processor, or a shipping carrier — buys, buybacks, settlement, and shipping rates are all
simulated. (Destination address validation is the one exception:
POST /ship/quoteruns the same real address check that live does.) - No real inventory reservation. Live draws and reserves physical inventory rows under a database lock so a card is never handed to two buyers; the sandbox simulates the draw in memory with no row-locking, so it is not a faithful test of two buyers racing for the same card.
-
Weaker buy/init deduplication. The
Idempotency-Keyreplay cache behaves identically in both modes (a retry with the same key + body replays the first response). But the extra409 DUPLICATE_REQUESTguard against two concurrent same-key buys is live-only — in sandbox, two simultaneous in-flight buys with the same key can both succeed. Test retry replay in sandbox; test hard duplicate rejection with a live key. -
Sellback pricing is frozen. A sandbox
POST /vm/buybackpays thebuyback_pricefixed on the item at buy time; live prices from the item's current FMV. Don't assert on exact sandbox payouts. -
Shipping orders don't progress. A sandbox
POST /ship/requestcreates an order and leaves it there —tracking_number,tracking_url,shipped_at, anddelivered_atstay null for the life of the process, since no carrier actually moves the card. -
Scoped by
user_idonly. Sandbox state is isolated by theuser_idyou submit; a live key is additionally partner-scoped. Webhooks fire in both modes — register a sandbox endpoint to receive sandbox events (a sandbox event never reaches a live URL), so you can test your receiver for free.
Errors
Errors use one consistent JSON shape and a conventional HTTP status:
{ "error": "Claw machine is out of stock", "code": "OUT_OF_STOCK" }
error is a human-readable message; code is a stable, machine-readable token — branch on
code, not the message text. Input-validation failures use code: "VALIDATION_ERROR" and add
a details array listing every field problem at once:
{
"error": "Amount must be a whole number",
"code": "VALIDATION_ERROR",
"details": [{ "field": "amount", "message": "Amount must be a whole number" }]
}
Statuses follow the usual conventions: 400 bad request, 401 unauthenticated, 402
insufficient prepaid balance, 403 wrong environment for the credential, 404 not found, 409
idempotency conflict, 422 unprocessable. Each operation below documents the exact codes it can
return.
Webhooks
Rather than poll, subscribe to outbound events — shipping lifecycle, purchase settlement/failure,
and buyback settlement — by registering a URL in your partner dashboard. Phygitals then POSTs
each event to your endpoint. Deliveries are HMAC-signed (X-Phygitals-Signature) and sent
at-least-once with retries, so verify the signature and dedupe on the envelope's id (stable across
redeliveries — not data.idempotency_key, which is null when the originating request sent no key).
Register or update your endpoint under your partner dashboard → Webhooks
(/partners/<your-slug>/webhooks, owner/editor only). The full payloads, and the registration
walkthrough, are in the Webhooks section below.
Next steps
- Mint a sandbox key from your partner dashboard.
- Call
GET /api/partner/sandbox/v1/vm/availableto list packs. - Walk a full buy → item → shipment flow in sandbox.
- Register a webhook and confirm you receive events.
- Ask Phygitals to enable a live key when you're ready for production.
For long-form integration guides, see the linked Partner integration guides.