Storefront SDKyour design, the listing's facts.
Design the product page and even the checkout form your own way, bound live to a Wakato listing, while the payment always runs on Wakato.
The Storefront SDK lets a seller's page look however they want while the facts come from the listing. You never type a price, a stock count, a title or a photo address into a page. You mark where they go, the SDK fills them, and when the seller changes the listing in Commerce every page showing it redraws within a second. Nothing to republish.
Every page Wakato serves for a Space (its sites, a custom domain,
/product/<id>) already carries the SDK as window.Wakato. It is ready before
your own scripts run.
There are four ways to use it, from no code to a fully custom checkout.
1. Bind with marks
<section data-wakato-listing="PRODUCT_ID" class="drop">
<img data-wakato-field="photo" class="hero">
<h1 data-wakato-field="title"></h1>
<p class="price" data-wakato-field="price"></p>
<span data-wakato-field="stock"></span>
<div data-wakato-field="options"></div>
<button data-wakato-checkout class="buy">Take it home</button>
</section>data-wakato-listing="<product id>" marks a block. Inside it,
data-wakato-field is filled from that listing:
| Field | What it holds |
|---|---|
title, description, long_description | The listing's words |
price | The label: "$12", "From $40", "$9/month", "Free". Follows the picked option |
price_amount, currency | The number and the currency |
stock, stock_count | "3 left", "Sold out", or empty when stock is not counted |
shipping, shipping_from, free_shipping | The shop's shipping facts |
refund_policy, shipping_policy, shop_name | The shop's own words, empty when unset |
buy_label | "Buy now", "Book now", "Subscribe", "Sold out"… |
rating | "★ 4.8 (12)", empty until reviewed |
photo | On an <img> sets src; elsewhere a background. data-wakato-index="2" = the third photo |
photos | Filled with one <img> per photo for your own gallery |
variant | On a <select>: one option per variant, sold-out ones disabled |
options | One group of buttons per option, aria-pressed on the picked one |
An empty data-wakato-checkout inside the block becomes that listing's buy
button, carrying the picked variant. It is disabled when sold out.
The block itself wears data-wakato-available, data-wakato-stage
(live or draft) and data-wakato-ready for your CSS:
[data-wakato-available="false"] .buy { opacity: .4; }A price elsewhere on the page can be bound on its own:
<a href="#buy">Get yours for <span data-wakato-listing="PRODUCT_ID" data-wakato-field="price"></span></a>2. Read it in your own script
const p = await Wakato.store.product(PRODUCT_ID);
// { id, title, price, price_label, currency, photos, available, stock,
// option_types, variants, shipping, shop, buy_label, rating, url, … }
const stop = Wakato.store.onChange(PRODUCT_ID, (p) => draw(p)); // now, and after every change
await Wakato.store.products([ID1, ID2]);
Wakato.store.select(blockOrId, variantId); // drive the options from your own picker
Wakato.store.bind(element); // bind blocks you add later (also automatic)The page also hears wakato:listing-change and wakato:variant-change on
window.
3. Your own checkout form
Quote
Wakato.checkout.quote() is the checkout's own arithmetic: the variant's
price, stock, where the seller ships, the discount and the tax. Show its
numbers; never compute a total in the page.
const q = await Wakato.checkout.quote({
productId: PRODUCT_ID,
variantId,
quantity: 2,
address: { name, line1, city, state, postalCode, country: "US" },
code: "SPRING10",
});
// { ok, missing: ["address"], problem, needsAccount, test, free, currency,
// subtotal, discount, shipping, tax, total, cents: {…}, labels: {…} }problem is a sentence you can show as is ("This seller does not ship to
Mali."). Call quote() again whenever the form changes.
Keep the total live
When the seller changes the listing, the SDK asks your last question again. Bind the numbers with marks and they follow with no code:
<button class="pay">Pay <span data-wakato-quote="total"></span></button>
Shipping <span data-wakato-quote="shipping"></span> · Tax <span data-wakato-quote="tax"></span>
<p data-wakato-quote="problem"></p>data-wakato-quote is one of total, subtotal, shipping, tax,
discount or problem, anywhere on the page. Or draw them yourself with
Wakato.checkout.onQuote(cb) or the wakato:quote-change event. Both fire
only when the numbers moved.
The card field
Wakato.checkout.paymentField("#card", {
color: "#F2F0EA", background: "#17171B", border: "1px solid #33333A",
primary: "#C6F432", radius: "10px",
font: "Instrument Sans, sans-serif",
fontUrl: "https://fonts.googleapis.com/css2?family=Instrument+Sans",
});This places Wakato's secure card field inside your element, in your colours. The card number is typed into that frame and never reaches the page's code. It is the one piece of the checkout that is not yours. Never collect card details in your own inputs.
Pay
const r = await Wakato.checkout.pay({
productId: PRODUCT_ID, variantId, quantity: 2, address, code,
buyer: { email: "ada@example.com", name: "Ada" }, // email is required
});
switch (r.status) {
case "paid": showThanks(r.orderId); break;
case "price_changed": refreshQuote(); break; // nothing was charged
default: showError(r.message); // declined, problem, error
}pay() quotes once more and charges exactly that quote through Wakato's
checkout. If the total moved since the page showed it, nothing is charged and
the status is price_changed. The order, the held money, the receipt, any job
and automations, and the payout then happen as for any other sale. The page
also hears wakato:checkout-paid with the orderId.
A free order needs no card field. When quote() answers needsAccount (a
subscription, or something delivered into a Wakato account), open the
ready-made checkout instead with Wakato.checkout(PRODUCT_ID).
Drafts are tests
A listing still in Draft is a rehearsal: the card field accepts only Wakato's test cards and no money moves. Make it Live when you want real payments.
4. The ready-made checkout
Any element with data-wakato-checkout="<product id>" (and optional
data-wakato-variant), or Wakato.checkout(id, { variantId }) from your
script, opens Wakato's own checkout over the page: address, card,
everything. The page hears wakato:checkout-open, wakato:checkout-close and
wakato:checkout-paid. Colour it with the set_checkout_branding tool.
<button data-wakato-checkout="PRODUCT_ID">Buy now</button>The public API behind it
The SDK reads from storefront-api, a public door that needs no key and answers
any origin. It only reads; nothing is created.
curl -X POST https://api.wakato.io/functions/v1/storefront-api \
-H "Content-Type: application/json" \
-d '{"action":"products","ids":["PRODUCT_ID"]}'
curl -X POST https://api.wakato.io/functions/v1/storefront-api \
-H "Content-Type: application/json" \
-d '{"action":"quote","product_id":"PRODUCT_ID","quantity":1,
"shipping_address":{"country":"US","postal_code":"30303"}}'products returns each listing's public facts (live and draft listings only).
quote returns the same arithmetic as Wakato.checkout.quote(). Paying is only
possible through the card field or the ready-made checkout.
Rules that keep a page honest
- The listing is the truth. Never hard-code what a mark can give.
- Never promise free shipping, a discount, a return policy or a delivery time
the shop has not set. Bind
free_shipping,refund_policyandshipping_policy; empty means say nothing. - Never put a price or stock in
<title>or meta tags: they cannot follow the listing. - Product card and product page templates can use all of this too:
data-wakato-listing="{{id}}"works inside a template.
Agents building a page read the same rules with the MCP tool
get_storefront_sdk_reference. See the commerce tools
and Payments.
App SDK
Space Apps run inside Wakato with the window.wakato SDK: their own records, the Space's real tables and contacts, mail, the device, AI models and payments, each behind a capability.
AI Gateway
An OpenAI-compatible endpoint for the models a Space allows, called with a Space key and billed to the Space wallet.