zn_live_ key on its next poll — along with the wallet balance and a $5 starter credit big enough to pay for one real order.
Nothing the agent built is lost: the sandbox it was working against folds into the approving account, and the sandbox key keeps working for test mode.
Agents: install the universal-checkout skill —
npx skills add zincio/skills --skill universal-checkout — and follow its Getting started section, which carries these calls step by step. This page explains the flow to the person who has to click Allow.The approval flow
1
The agent asks for a key
human_message is written to be shown to a person as-is. device_code is the agent’s half of the handshake and should never be shown to anyone.2
The human approves it
They open The page names the agent, says whether a sandbox is attached and whether a starter credit is on the table, and offers Allow or Deny. Clicking Allow issues the key.
verification_uri_complete in any browser — their laptop, their phone, a machine the agent has never touched — and sign in with Google, GitHub, or an email magic link.The magic link brings them back to the approval even if they open it in a different browser than the one they started in, so the link can be forwarded or read on a phone.
3
The agent polls for the key
interval seconds (5). While waiting, the response is an RFC 8628–shaped error:4
Success
api_key — the same field name the sandbox mint used. sandbox is absent when no sandbox key was attached.The starter credit
The first time an account approves an agent whose attached sandbox actually placed an order, $5 is added to the wallet. Once per account, not once per agent. Thestarter_credit object is on every key-issuing response whether or not a credit landed, so an agent never has to infer its budget:
1 fee, so the item plus tax and shipping has to come in under $4. That’s what
max_price_cents is — use it, don’t recompute it.Your first live order
1
Find something in budget
GET /search takes the same price ceiling your order will:max_price and min_price are in cents — the same number you’ll send as max_price on POST /orders, so every result you get back fits the order you intend to place. Results priced above the ceiling are dropped, and so are results whose price isn’t known: a filter that lets through “price unknown” isn’t a filter.The response carries excluded_by_price (how many orderable results the clamp removed) and, when the clamp emptied the list entirely, a hint saying so. Each result’s url is what you pass to POST /orders.Search is metered against the wallet, so on a $5 balance it’s worth searching once with a good query rather than fanning out over variations. Or skip it:
starter_credit.picks already carries orderable URLs.2
Place it
max_price at or under starter_credit.max_price_cents. The full shipping_address is required, including phone_number — retailers reject orders without one. Phone numbers come back normalized to E.164 (+14155552671).3
Wait — for real this time
pending for minutes while a worker actually places it at the retailer — unlike the sandbox, where it advances in a second. Poll GET /orders/{id}, or register a webhook. order_placed is the success terminal for the order itself; shipping progress then shows up under tracking_numbers[].When the wallet runs out
POST /orders refuses with 402 and the code insufficient_funds. The numbers and the way forward are in error.details:
fund_with lists three rails, in order of least human involvement:
Check the balance any time with
GET /wallet/me.
Claiming without a device code
Theclaim_url from the mint response still works as an alternative human step. Hand it over:
“I’ve built and tested the ordering flow against a Zinc sandbox. To adopt it into your account, sign in here: https://app.zinc.com/claim/...”
Signing in there folds the sandbox — order history and key — into their Zinc account, and the sandbox key keeps working. What it doesn’t do is give the agent a live key: claiming moves ownership, not the ability to spend. Use it when the human wants to adopt the work now and decide about live access later.
An agent that handed over a URL can’t see what happened next, so poll:
Seeing the account’s keys
GET /api-keys called with an API key returns the caller’s own key in full and every other key masked to its prefix and last four characters (zn_live_…ab12) — enough to recognise a key in a list, useless as a credential. A human signed into the dashboard sees full keys there.
A key that could read its siblings’ secrets could become any of them. The device flow hands keys to agents an account owner has only just met, so that door is closed.
What changes when you switch
The request and response shapes don’t change. The environment around them does:Before your first live order
- Handle both failure timings. Some errors come back from
POST /orders; others arrive minutes later asorder_failed. See Error Handling. - Set
max_priceto cover the real total. Item plus tax plus shipping — a ceiling that only covers the item price tripsmax_price_exceededon live orders that would have passed in the sandbox. - Re-register webhooks. Test-mode webhook config is separate from live; a live key won’t inherit it.
- Send an idempotency key. On live orders, a retry without one is a second order. See Idempotency.
- Start small. One cheap real order end to end before you turn on volume — which is exactly what the starter credit is for.
Reference
Wallet
Balances, funding, and the order fee schedule.
Get Sandbox Status
GET /sandbox/status — every response shape.
