Your store
Every business has one site: its booking page, and its online store once something is for sale online. You edit a draft (pages, brand kit, menus). Nobody sees it until you publish. Tuppence then checks every block and every sentence, freezes the result as the next version and puts it live. Any earlier version can go live again.
- Edit the draft
POST /v1/sites/{id}/pages/{page} - Publish
POST /v1/sites/{id}/publish - Liveversion
N,store.site_published - Go back
…/versions/{n}/revert
1. Find your site
Section titled “1. Find your site”curl -sS https://api.tuppence.ai/v1/sites \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY"import { Tuppence } from "@tuppence/node";
const tuppence = new Tuppence(process.env.TUPPENCE_SECRET_KEY);const { data: [site] } = await tuppence.request("GET", "/v1/sites");console.log(site.id, site.published_version);import osfrom tuppence import Tuppence
tuppence = Tuppence(os.environ["TUPPENCE_SECRET_KEY"])site = tuppence.request("GET", "/v1/sites")["data"][0]print(site["id"], site["published_version"])The site is made from your booking page the first time anyone asks, so there is always one.
published_version is what the public sees (null before the first publish).
2. Edit the draft
Section titled “2. Edit the draft”A page has a kind (home, page, product_template, collection_template, cart, checkout,
order_status, links or not_found), a title and up to 80 blocks. A page also has a
slug (/pages/about-us) and may have its own path (/about).
curl -sS https://api.tuppence.ai/v1/sites/site_…/pages \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"kind": "page", "slug": "about-us", "path": "/about", "title": "About us", "blocks": [{"id": "intro", "type": "rich_text", "props": {"body": [{"type": "paragraph", "content": [{"text": "Made by hand in Whitby."}]}]}}]}'const page = await tuppence.request("POST", `/v1/sites/${site.id}/pages`, { body: { kind: "page", slug: "about-us", path: "/about", title: "About us", blocks: [{ id: "intro", type: "rich_text", props: { body: [{ type: "paragraph", content: [{ text: "Made by hand in Whitby." }] }] } }], },});page = tuppence.request("POST", f"/v1/sites/{site['id']}/pages", body={ "kind": "page", "slug": "about-us", "path": "/about", "title": "About us", "blocks": [{"id": "intro", "type": "rich_text", "props": {"body": [{"type": "paragraph", "content": [{"text": "Made by hand in Whitby."}]}]}}],})POST /v1/sites/{id}/pages/{page} changes a draft page (only the fields you send).
POST /v1/sites/{id} changes the site’s brand kit and navigation (its header and footer
links). All of these change the draft only.
A path the store uses itself (/cart, /checkout, /products/…, /mcp and the like) is
refused with the path it clashes with.
3. Preview it
Section titled “3. Preview it”POST /v1/sites/{id}/preview_token answers a token and a url, shown once. The URL opens the
draft as it is now, never cached and never indexed. Minting another token retires the last.
4. Publish
Section titled “4. Publish”curl -sS https://api.tuppence.ai/v1/sites/site_…/publish \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"note": "Spring range"}'const version = await tuppence.request("POST", `/v1/sites/${site.id}/publish`, { body: { note: "Spring range" } });console.log(version.number, version.live);version = tuppence.request("POST", f"/v1/sites/{site['id']}/publish", body={"note": "Spring range"})print(version["number"], version["live"])Before anything goes live, Tuppence checks:
| Check | Refused with |
|---|---|
| Every block against its own rules, prices shown all-in among them | 400, naming the page and the block |
| Every sentence against your products’ facts (a claim needs evidence) | 409, naming the sentence, with a link to its block |
| The version fits in 2 MB | 400, naming the biggest page |
A refused publish changes nothing. Once it passes, the draft is frozen as version N, goes live,
and store.site_published is sent. The first publish with a product online also opens the
store, and / becomes its home.
POST /v1/sites/{id}/schedule with at (unix seconds, up to a year ahead) checks and freezes
the draft now and puts it live at that time. DELETE /v1/sites/{id}/schedule cancels it.
5. Go back
Section titled “5. Go back”GET /v1/sites/{id}/versions lists every version with live and scheduled_at.
POST /v1/sites/{id}/versions/{version}/revert (by id or number) puts an earlier version live
again, exactly as it was. The draft is not changed.
Each publish is also on your change history (GET /v1/change_sets?object_type=site), and
undoing it puts the version before back live. Once the site has been published again since, the
undo is refused with 409 change_set_conflict.
Agents and the assistant
Section titled “Agents and the assistant”The same steps are tools over MCP and actions for the Tuppence Assistant:
get_site, list_site_versions, update_site_page, update_brand_kit, update_navigation,
publish_site, schedule_site and revert_site. Edits go to the draft, so nothing an agent
writes is live until publish_site. In the assistant, publishing always asks first and shows
what changes, page by page.
No tool deletes the site, changes its domains, its trader details or its payment methods. A person does those in the dashboard.
Events
Section titled “Events”| Event | When |
|---|---|
store.site_updated | The site’s own fields changed |
store.site_published | A version went live (a publish, or a schedule’s time) |
store.site_scheduled | A publish was scheduled |
store.site_reverted | An earlier version went live again |
Every store page
Section titled “Every store page”For developers:
- Selling products online
- Store checkout
- Store orders
- Shipping and delivery
- Packing and labels
- Returns and exchanges
- Tax on your online store
- Your store’s policies
- Selling abroad
For the people who run the store, the help centre’s online store guides: