Hanzo AI

Commerce

Selling: checkout, subscriptions, invoices, spend alerts, payment webhooks and the storefront catalog.

Also for this capability: API · CLI · MCP · SDKs

Selling: checkout, subscriptions, invoices, spend alerts, payment webhooks and the storefront catalog.

Base URLhttps://api.hanzo.ai
Operations180
AuthAuthorization: Bearer $HANZO_API_KEY

Specification

HIP-1220 · Commerce — The Merchant Half — Draft · read the specification →

/v1/commerce is selling: checkout, subscriptions, invoices, spend alerts, payment webhooks, the storefront and its catalog, carts, priced SKUs and the typed payment door. It is hanzoai/cloud apps/commerce, which mounts the hanzoai/commerce module natively on the cloud's own router — one router, one specificity space, zero handler adaptation (apps/commerce/mount.go:3-14).

This HIP states the target surface — one root for every merchant noun — and the boundary with billing, the customer money door HIP-0018 declares: commerce is the merchant half and the store it owns; billing is the address a customer's money questions are answered at.

Motivation

The app answers at eight top-level roots today (manifest/apps.go:116), which puts a cart, a storefront and a payment in three different generated client classes for one subsystem. HIP-0139 §7.1's default closes that: an app with one store is one capability, however many nouns it answers for.

Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.

§1 One root

Every route commerce serves MUST be under /v1/commerce:

  • /v1/commerce/cart — the shopper's basket (from /v1/cart; HIP-1002).
  • /v1/commerce/catalog — the merchant catalog rows in commerce's store, merging the SuperAdmin CRUD now at /v1/catalog/*.
  • /v1/commerce/payments — the typed payment door (from /v1/payments; HIP-1005).
  • /v1/commerce/plans — plan ROWS in commerce's datastore (from /v1/plans/{entries,seed}). The /v1/plans root is the plans capability's; these rows are not that catalog and take commerce's prefix.
  • /v1/commerce/store — storefronts, listings, checkout (from /v1/store; HIP-1006).
  • the tenant-admin surface, from /_/commerce — HIP-0139 §3.3, nothing outside /v1; same audience, same binary. This half of the fold lands in the hanzoai/commerce module, which registers those routes (one module release).

Where the router still serves the old spellings, the pairs are the commerce lines in cloud's openapi/misfiled.txt, and each closes by fold, never alias.

§2 The billing boundary

Commerce MUST NOT serve /v1/billing. That address is billing's (HIP-0018), answered over the plane; commerce keeps the store and publishes the plane operations — balance, the prepaid gate, credit, usage, transactions, spend and scope rules (apps/commerce/mount.go:204-210) — that the money door and every metered surface debit through. One store, one publisher, one address that is somebody else's.

§3 The store it owns

Per-tenant merchant and money stores under <DataDir>/commerce (apps/commerce/mount.go:319-323), encrypted under the process master key when the sqlcipher codec is linked. The at-rest posture is decided in exactly one function (apps/commerce/mount.go:161-170): a production build refuses to open money data unencrypted, and a pure-Go dev build gets the module's documented unencrypted dev store rather than no money plane at all.

The customer ledger of record is NOT here: EmbedConfig.Ledger injects apps/finance, so a credit minted by a settled charge lands in the one ledger (apps/commerce/mount.go:6-8).

§4 Tenant

The paying org is read from the validated principal (HIP-0026) and never from a request field — a field is caller-supplied, and an org read from one is a cross-tenant write the caller asserted for itself (apps/commerce/payments.go:25-30). A card payment taken here can only credit the caller's own org.

§5 Price, and the screen on the mint

The surface is free, in those words: Price: cloud.Free (plugin/commerce/main.go:21) — it is the path to payment itself. The meter downstream of the edge is the one §2 publishes.

Both doors that mint spendable balance from a settled charge — the browser top-up and the typed payment op — MUST wrap their handler, not their route, with the one risk screen (apps/commerce/mount.go, the credit-screen note; apps/commerce/risk.go): a typed op is projected four ways and only the handler is the point all four run through.

§6 Events and observability

No commerce.<noun>.<verb> events reach the platform bus, so a customer's webhooks (HIP-1310) receive nothing from it directly. A settled charge is stated once, as the analytics event order_completed through the same capture core POST /v1/event reaches (apps/commerce/emit.go:24-31, :58, :80) — counted by the same lenses, forwarded by the same destinations fan-out. The emit is detached and bounded: a conversion row is expendable, a settled payment is not. Beyond the request span every route gets, it emits nothing.

§7 Typed and declared

The operations this repo owns are typed: payments, invoices, cart, the health probe and the plane ops. The embedded module's surface is declared with prose beside the live route (apps/commerce/describe.go:3-14), which cannot add an operation, only explain one that exists. Each declared route is a typing the module still owes — the honest typed op exports a value-taking core from the module first — not a route that can never be typed (apps/commerce/mount.go, the module-handler note).

§8 Stage and upstream

The stage is ga — the manifest row declares none, and absent is ga (HIP-0139 §8).

It derives from hanzoai/commerce (pinned v1.50.58 in cloud's go.mod:18), dual-licensed MIT / Apache-2.0. The whole module survives in HEAD — routes, datastore, middleware — embedded in-process. PAN-touching paths relay to the out-of-process Payments/Vault; this binary holds tokens and intent ids only and is not in PCI-DSS scope (apps/commerce/mount.go:16-19).

Rationale

The alternative to the fold is a split into cart, store, payments and catalog capabilities. All of them read and write commerce's one datastore, so the split is four apps on one store — the defect HIP-0139 §7.2 refuses by name. The nouns stay; they become path segments of the app that owns their rows.

Security Considerations

The wrong implementation hands an attacker the mint. A screen mounted on the router instead of the handler leaves the same authorized deposit reachable unscreened through the tool projection; an org taken from a request field turns a card payment into a cross-tenant credit; a per-config at-rest posture lets a production build quietly write plaintext money data where the per-build decision in §3 refuses to boot.

Four surfaces

SurfaceReaches this capability asCoverage
RESTcommerce at its own prefix180 operations
CLIhanzo commerce …162 of 180 — the CLI pins the document on its own clock
SDKCommerceApi in every published client178 of 180 — the clients are generated at their own release
MCPtool commerce on https://api.hanzo.ai/v1/mcp215 operations, 76 under the document's own id — ask describe for the rest

Quickstart

export HANZO_API_KEY=sk-...   # console.hanzo.ai → API keys

Then the first call — a read that needs nothing but the key. GET /v1/commerce/org, operation get_commerce_org:

hanzo commerce org

Endpoints

EndpointWhat it does
POST /v1/commerce/cart/{id}/discardDiscard a cart the shopper abandoned
POST /v1/commerce/cart/{id}/itemSet one item's quantity in a cart; zero removes it
GET /v1/commerce/cart/{id}Read one cart with its lines and totals
POST /v1/commerce/cartOpen a cart for a shopper to fill
GET /v1/commerce/catalog/entriesThe raw catalog entries, including the unpublished ones
POST /v1/commerce/catalog/entriesAdd a catalog entry
POST /v1/commerce/catalog/models/refreshRefresh the model catalog by reading the upstream provider
POST /v1/commerce/catalog/modelsLand a syncer's view of the model catalog: upstream costs and machine facts
POST /v1/commerce/catalog/seedSeed the embedded catalog, without disturbing edits already made
GET /v1/commerce/catalogThe public product catalog projection for a brand
GET /v1/commerce/collection/{collectionid}Fetch one collection
POST /v1/commerce/collection/{collectionid}Method-override tunnel for a collection — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/collection/{collectionid}Replace a collection outright
PATCH /v1/commerce/collection/{collectionid}Change part of a collection
DELETE /v1/commerce/collection/{collectionid}Delete a collection, keeping a recoverable copy
GET /v1/commerce/collection/List your org's collections, as a page
POST /v1/commerce/collection/Create a collection
GET /v1/commerce/currenciesThe reference currency list the price and settings pickers render
GET /v1/commerce/depositsRead the crypto deposit watcher's runtime state, asset by asset
GET /v1/commerce/disclosure/{disclosureid}Fetch one disclosure
POST /v1/commerce/disclosure/{disclosureid}Method-override tunnel for a disclosure — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/disclosure/{disclosureid}Replace a disclosure outright
PATCH /v1/commerce/disclosure/{disclosureid}Change part of a disclosure
DELETE /v1/commerce/disclosure/{disclosureid}Delete a disclosure, keeping a recoverable copy
GET /v1/commerce/disclosure/List your org's disclosures, as a page
POST /v1/commerce/disclosure/Create a disclosure
GET /v1/commerce/discount/{discountid}Fetch one discount
POST /v1/commerce/discount/{discountid}Method-override tunnel for a discount — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/discount/{discountid}Replace a discount outright
PATCH /v1/commerce/discount/{discountid}Change part of a discount
DELETE /v1/commerce/discount/{discountid}Delete a discount, keeping a recoverable copy
GET /v1/commerce/discount/List your org's discounts, as a page
POST /v1/commerce/discount/Create a discount
GET /v1/commerce/healthAnswers ok whenever the commerce subsystem is mounted.
GET /v1/commerce/movie/{movieid}Fetch one movie
POST /v1/commerce/movie/{movieid}Method-override tunnel for a movie — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/movie/{movieid}Replace a movie outright
PATCH /v1/commerce/movie/{movieid}Change part of a movie
DELETE /v1/commerce/movie/{movieid}Delete a movie, keeping a recoverable copy
GET /v1/commerce/movie/List your org's movies, as a page
POST /v1/commerce/movie/Create a movie
GET /v1/commerce/note/{noteid}Fetch one note
POST /v1/commerce/note/{noteid}Method-override tunnel for a note — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/note/{noteid}Replace a note outright
PATCH /v1/commerce/note/{noteid}Change part of a note
DELETE /v1/commerce/note/{noteid}Delete a note, keeping a recoverable copy
GET /v1/commerce/note/List your org's notes, as a page
POST /v1/commerce/note/Create a note
GET /v1/commerce/orgThe public org configuration a checkout page boots from
GET /v1/commerce/payments/{id}Read one settled payment by its id
POST /v1/commerce/paymentsTake a card payment and credit the org's balance
PUT /v1/commerce/plans/entries/{slug}Edit a plan, leaving the fields you omit alone
DELETE /v1/commerce/plans/entries/{slug}Remove a plan from the authority
GET /v1/commerce/plans/entriesThe raw plan authority rows
POST /v1/commerce/plans/entriesAdd a subscription plan
POST /v1/commerce/plans/seedSeed the embedded plan catalog, without overwriting administrative edits
GET /v1/commerce/product/{productid}Fetch one product
POST /v1/commerce/product/{productid}Method-override tunnel for a product — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/product/{productid}Replace a product outright
PATCH /v1/commerce/product/{productid}Change part of a product
DELETE /v1/commerce/product/{productid}Delete a product, keeping a recoverable copy
GET /v1/commerce/product/List your org's products, as a page
POST /v1/commerce/product/Create a product
PUT /v1/commerce/rates/entries/{product}/{meter}Edit a rate, and mark it as operator-set
DELETE /v1/commerce/rates/entries/{product}/{meter}Remove a rate outright
GET /v1/commerce/rates/entriesList what one unit of each metered thing costs
POST /v1/commerce/rates/entriesAdd a rate
POST /v1/commerce/rates/importLoad the published price document, reconciling rather than replacing
GET /v1/commerce/return/{returnid}Fetch one return
POST /v1/commerce/return/{returnid}Method-override tunnel for a return — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/return/{returnid}Replace a return outright
PATCH /v1/commerce/return/{returnid}Change part of a return
DELETE /v1/commerce/return/{returnid}Delete a return, keeping a recoverable copy
GET /v1/commerce/return/List your org's returns, as a page
POST /v1/commerce/return/Create a return
GET /v1/commerce/saleschannel/{saleschannelid}Fetch one sales channel
POST /v1/commerce/saleschannel/{saleschannelid}Method-override tunnel for a sales channel — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/saleschannel/{saleschannelid}Replace a sales channel outright
PATCH /v1/commerce/saleschannel/{saleschannelid}Change part of a sales channel
DELETE /v1/commerce/saleschannel/{saleschannelid}Delete a sales channel, keeping a recoverable copy
GET /v1/commerce/saleschannel/List your org's sales channels, as a page
POST /v1/commerce/saleschannel/Create a sales channel
GET /v1/commerce/stocklocation/{stocklocationid}Fetch one stock location
POST /v1/commerce/stocklocation/{stocklocationid}Method-override tunnel for a stock location — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/stocklocation/{stocklocationid}Replace a stock location outright
PATCH /v1/commerce/stocklocation/{stocklocationid}Change part of a stock location
DELETE /v1/commerce/stocklocation/{stocklocationid}Delete a stock location, keeping a recoverable copy
GET /v1/commerce/stocklocation/List your org's stock locations, as a page
POST /v1/commerce/stocklocation/Create a stock location
POST /v1/commerce/store/{storeid}/authorize/{orderid}Authorize an order that already exists, holding the funds without settling them
POST /v1/commerce/store/{storeid}/authorizeAuthorize a new order against a storefront, holding the funds without settling them
GET /v1/commerce/store/{storeid}/bundle/{key}Fetch a bundle as this storefront sells it
POST /v1/commerce/store/{storeid}/capture/{orderid}Capture a previously authorized order and settle the payment
POST /v1/commerce/store/{storeid}/chargeAuthorize and capture a new order in one call
POST /v1/commerce/store/{storeid}/checkout/authorize/{orderid}Authorize an existing order, holding the funds — the checkout spelling
POST /v1/commerce/store/{storeid}/checkout/authorizeAuthorize a new order against a storefront, holding the funds — the checkout spelling
POST /v1/commerce/store/{storeid}/checkout/capture/{orderid}Capture a previously authorized order and settle it — the checkout spelling
POST /v1/commerce/store/{storeid}/checkout/chargeAuthorize and capture a new order in one call — the checkout spelling
POST /v1/commerce/store/{storeid}/checkout/paypal/cancel/{payKey}PayPal cancel by pay key — refuses, exactly as the unprefixed address does
POST /v1/commerce/store/{storeid}/checkout/paypal/confirm/{payKey}PayPal confirm by pay key — refuses, exactly as the unprefixed address does
POST /v1/commerce/store/{storeid}/checkout/paypal/payStart a PayPal authorization for a new order — the checkout spelling
GET /v1/commerce/store/{storeid}/listing/{key}Fetch one listing override, by item id or by its slug or SKU
POST /v1/commerce/store/{storeid}/listing/{key}Add a listing override under a new key
PUT /v1/commerce/store/{storeid}/listing/{key}Upsert a listing override
PATCH /v1/commerce/store/{storeid}/listing/{key}Confirm a listing override exists and re-save the store
DELETE /v1/commerce/store/{storeid}/listing/{key}Remove a listing override
GET /v1/commerce/store/{storeid}/listingThe storefront's whole listing override map
POST /v1/commerce/store/{storeid}/paypal/cancel/{payKey}PayPal cancel by pay key — refuses, because a pay key alone does not identify the order
POST /v1/commerce/store/{storeid}/paypal/confirm/{payKey}PayPal confirm by pay key — refuses, because a pay key alone does not identify the order
POST /v1/commerce/store/{storeid}/paypal/payStart a PayPal authorization for a new order
GET /v1/commerce/store/{storeid}/product/{key}Fetch a product as this storefront sells it
POST /v1/commerce/store/{storeid}/trialStart this store's no-card trial on the entry plan
GET /v1/commerce/store/{storeid}/variant/{key}Fetch a variant as this storefront sells it
GET /v1/commerce/store/{storeid}Fetch one storefront
POST /v1/commerce/store/{storeid}Method-override tunnel for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/store/{storeid}Replace a storefront outright
PATCH /v1/commerce/store/{storeid}Change part of a storefront
DELETE /v1/commerce/store/{storeid}Delete a storefront, keeping a recoverable copy
GET /v1/commerce/store/List your org's storefronts as a page
POST /v1/commerce/store/Create a storefront
GET /v1/commerce/store/accessWhether a store is entitled to trade, and why
GET /v1/commerce/store/currentResolve your org's active storefront without naming an id
POST /v1/commerce/store/tokenMint your org's least-privilege storefront read key
GET /v1/commerce/submission/{submissionid}Fetch one submission
POST /v1/commerce/submission/{submissionid}Method-override tunnel for a submission — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/submission/{submissionid}Replace a submission outright
PATCH /v1/commerce/submission/{submissionid}Change part of a submission
DELETE /v1/commerce/submission/{submissionid}Delete a submission, keeping a recoverable copy
GET /v1/commerce/submission/List your org's submissions, as a page
POST /v1/commerce/submission/Create a submission
GET /v1/commerce/subscriber/{subscriberid}Fetch one subscriber
POST /v1/commerce/subscriber/{subscriberid}Method-override tunnel for a subscriber — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/subscriber/{subscriberid}Replace a subscriber outright
PATCH /v1/commerce/subscriber/{subscriberid}Change part of a subscriber
DELETE /v1/commerce/subscriber/{subscriberid}Delete a subscriber, keeping a recoverable copy
GET /v1/commerce/subscriber/List your org's subscribers, as a page
POST /v1/commerce/subscriber/Create a subscriber
GET /v1/commerce/tokentransaction/{tokentransactionid}Fetch one token transaction
POST /v1/commerce/tokentransaction/{tokentransactionid}Method-override tunnel for a token transaction — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/tokentransaction/{tokentransactionid}Replace a token transaction outright
PATCH /v1/commerce/tokentransaction/{tokentransactionid}Change part of a token transaction
DELETE /v1/commerce/tokentransaction/{tokentransactionid}Delete a token transaction, keeping a recoverable copy
GET /v1/commerce/tokentransaction/List your org's token transactions, as a page
POST /v1/commerce/tokentransaction/Create a token transaction
GET /v1/commerce/transfer/{transferid}Fetch one transfer
POST /v1/commerce/transfer/{transferid}Method-override tunnel for a transfer — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/transfer/{transferid}Replace a transfer outright
PATCH /v1/commerce/transfer/{transferid}Change part of a transfer
DELETE /v1/commerce/transfer/{transferid}Delete a transfer, keeping a recoverable copy
GET /v1/commerce/transfer/List your org's transfers, as a page
POST /v1/commerce/transfer/Create a transfer
GET /v1/commerce/variant/{variantid}Fetch one variant
POST /v1/commerce/variant/{variantid}Method-override tunnel for a variant — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/variant/{variantid}Replace a variant outright
PATCH /v1/commerce/variant/{variantid}Change part of a variant
DELETE /v1/commerce/variant/{variantid}Delete a variant, keeping a recoverable copy
GET /v1/commerce/variant/List your org's variants, as a page
POST /v1/commerce/variant/Create a variant
GET /v1/commerce/wallet/{walletid}Fetch one wallet
POST /v1/commerce/wallet/{walletid}Method-override tunnel for a wallet — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/wallet/{walletid}Replace a wallet outright
PATCH /v1/commerce/wallet/{walletid}Change part of a wallet
DELETE /v1/commerce/wallet/{walletid}Delete a wallet, keeping a recoverable copy
GET /v1/commerce/wallet/List your org's wallets, as a page
POST /v1/commerce/wallet/Create a wallet
GET /v1/commerce/watchlist/{watchlistid}Fetch one watchlist
POST /v1/commerce/watchlist/{watchlistid}Method-override tunnel for a watchlist — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/watchlist/{watchlistid}Replace a watchlist outright
PATCH /v1/commerce/watchlist/{watchlistid}Change part of a watchlist
DELETE /v1/commerce/watchlist/{watchlistid}Delete a watchlist, keeping a recoverable copy
GET /v1/commerce/watchlist/List your org's watchlists, as a page
POST /v1/commerce/watchlist/Create a watchlist
GET /v1/commerce/webhook/{webhookid}Fetch one webhook
POST /v1/commerce/webhook/{webhookid}Method-override tunnel for a webhook — for clients that cannot send PUT, PATCH or DELETE
PUT /v1/commerce/webhook/{webhookid}Replace a webhook outright
PATCH /v1/commerce/webhook/{webhookid}Change part of a webhook
DELETE /v1/commerce/webhook/{webhookid}Delete a webhook, keeping a recoverable copy
GET /v1/commerce/webhook/List your org's webhooks, as a page
POST /v1/commerce/webhook/Create a webhook
POST /v1/commerce/webhooks/{provider}Payment-provider webhook intake for settlement and subscription lifecycle events

All Hanzo APIs · Interactive reference

How is this guide?