shop_publish decision. Money moves
only when a buyer completes air’s Stripe Connect checkout, and the storefront
changes only when the owner approves the decision in Needs you.
Stage 0: plan-only dry runs (anywhere, no credentials)
merch-drop and event-ticket ship inside the @wzrdtech/zap package, so
this works from an empty directory:
statusisplannedandliveisfalse: nothing was written anywhere.- The
commerce.stage_listingstep hasquoteUsd: 0. The only quoted spend is theimage.genstep. wouldStagedescribes the listing (name,kind,priceCents,inventory, the image step it uses) and carriescharges: falseandrequiresOwnerApproval: true.
LOCAL_STEP_FAILED before any provider work, because the
catalog lives in the creator’s box. Plan-only works everywhere.
Repeat Stage 0 with real inputs (--input PRODUCT_NAME=... --input PRICE_CENTS=...)
until the wouldStage block reads exactly like the listing the creator wants.
There is no cost to iterating here.
Stage 1: one creator, merch-drop, live, in their box
Pick one creator whose air box is running and who has completed Stripe Connect onboarding (their merchant row showscharges_enabled). Everything in this
stage happens inside that box, where OPENAI_BASE_URL and OPENAI_API_KEY are
the air gateway environment the CLI resolves automatically.
1 commerce item(s) staged for owner approval; nothing was charged. and the
stage_listing step carries status: staged, a decisionId, and
charges: false.
Then, in order:
- Inspect the catalog.
cat ~/.hermes/miniapps/shop/catalog.jsonshows the new item with animageUrlon the media host. Re-running the Zap with the samePRODUCT_NAMEreplaces the entry by key rather than duplicating it. - Approve in Needs you. The
shop_publishdecision appears in the owner’s Needs you queue (and, on iMessage, as a card that flips to Approved in place). Until this tap, buyers see nothing and nothing can be charged. - Verify the storefront. Open the creator’s shop mini-app. The listing is
present, the price matches
PRICE_CENTS, the image renders. The price the buyer sees comes fromstorefront_products, projected at approval; the Zap’s number was only a proposal. - Make one real purchase with a Stripe test card if the account is in test
mode, or the smallest real price otherwise. Confirm the order flips to
paidonce and inventory decrements by one. That confirms the rails end to end without the Zap having touched any of them.
COMMERCE_INVALID_LISTING), a
missing gateway (COMMERCE_UNCONFIGURED), or an unreadable catalog
(COMMERCE_CATALOG_UNREADABLE) all stop before publish_catalog is sent. A
lost reply (COMMERCE_STAGE_TIMEOUT, or a COMMERCE_STAGE_FAILED that says
“may have filed the decision”) is the one exception: the listing stays in
catalog.json (the Zap upserts by key, so a rerun updates it rather than
duplicating it), and air may already hold the pending shop_publish decision,
so check Needs you before retrying. Only the catalog-listings agent’s
content edits roll back on a lost reply. The storefront is unchanged in every
failure case.
Stage 2: content edits through the agent
Once the first listing is live, let the same creator improve its copy through thecatalog-listings skill rather than by re-running the Zap:
shop_publish decision, and price and inventory
are not editable this way (re-run the Zap with new inputs). Watch for the
owner being asked to approve too often; if the agent stages one field at a
time, tell it to batch the edits into one change (up to 25 lines).
Stage 3: event-ticket
Openevent-ticket only after Stage 1 has produced a real paid order for a
physical listing, because tickets add fulfilment behaviour on the air side
(ticket codes and check-in) that a merch listing does not exercise.
- The catalog entry has
kind: event_ticketandinventoryequal toCAPACITY(ornullfor unlimited). - The listing name and description carry the date, time, and venue.
zap listings auditflags a ticket without them. - A test purchase yields a
ticket_codeon the order, and scanning it a second time is refused. Both are air’s existingcheckout.session.completedfulfilment; the Zap added no code to that path.
Stage 4: widen
After one creator has a paid merch order and one scanned ticket:- Add creators one box at a time. Every box runs the same bundled recipes; the only per-creator prerequisite is Stripe Connect onboarding.
- Keep
budget.cap_usdin both recipes small. Art is the only spend, and a cap in the low single digits covers it. - Encourage creators to
zap add zap-merch-dropand edit the prompt files rather than editingZap.mdstep kinds;zap validateandzap lintkeep the commerce step well-formed.
What not to do
- Do not add a payment step to a Zap, and do not accept a recipe that claims
to.
commerce.payment_requeststages a request; it does not collect. - Do not point
ZAP_AIR_API_BASEat a remote HTTP host to run commerce from outside a box. The CLI refuses plain HTTP off localhost, and off-box staging is not a supported path. - Do not tell a creator a listing is live when the run says
staged. It is live when the owner has approved and the storefront shows it. - Do not wire a second commerce rail (for example Whop) into
stage_listing. The step is rail-agnostic; if another rail is ever wanted, it is chosen atshop_publishapply time in air, not in the Zap.
