Skip to main content
Going live is one approval. The agent asks for a key, a human clicks Allow in a browser, and the agent picks up a 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

The sandbox key as a Bearer token is optional, but sending it attaches the sandbox: approval then claims its orders and its key onto the account, and it’s what makes the starter credit available.
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 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.
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.
3

The agent polls for the key

Poll every interval seconds (5). While waiting, the response is an RFC 8628–shaped error:
The key is returned exactly once. A second successful poll gets expired_token, so persist the key the moment it arrives.
4

Success

The key is under 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. The starter_credit object is on every key-issuing response whether or not a credit landed, so an agent never has to infer its budget:
5buyslessthanitlookslike:everyordercarriesa5 buys less than it looks like: every order carries a 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.
Today’s picks are a Mainstays Darby 16 oz stoneware mug (2.88)andLiptonblackteabags,24count(2.88) and Lipton black tea bags, 24 count (3.42), both on walmart.com. They’re chosen to land under the budget delivered, so the first real order is a formality rather than a research project. The account also gets the usual default Production and Test API keys created at first sign-in. The agent’s key is separate from both and named after the agent, so the human can see which key belongs to which agent — and revoke just that one — in Settings.

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

Set 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

A live order sits at 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:
On the human_card rail, the spend request must be for the 402 challenge amountmax_price plus the $1 order fee — not the item price. A request for less is declined at capture.
Check the balance any time with GET /wallet/me.

Claiming without a device code

The claim_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:
The claim token is single-use and lives only in that URL. Only its hash is stored, so it can’t be reproduced — if it’s lost, use the device flow above instead.

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:
Because the sandbox skips those validations, your first live order can fail in ways no test did — a rejected address, an unreachable URL, a real out-of-stock. Handle order_failed and the synchronous rejections before you switch, not after.

Before your first live order

  • Handle both failure timings. Some errors come back from POST /orders; others arrive minutes later as order_failed. See Error Handling.
  • Set max_price to cover the real total. Item plus tax plus shipping — a ceiling that only covers the item price trips max_price_exceeded on 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.