Selling products online
Your store sells the same products as your till. To sell one online, give it photos (each with a description for people who can’t see it), any web fields you want, and publish it online. Collections group products for your pages and menus.
- Photos
POST /v1/media - Web fields
POST /v1/products/{id} - Checklist
GET …/online_checklist - Online
POST …/publish_online
1. Upload its photos
Section titled “1. Upload its photos”POST /v1/media takes the file’s bytes as base64 in data: a JPEG or PNG photo (up to 10 MB,
re-encoded to at most 2048 px on the long edge) or an MP4 video (H.264, up to 60 seconds, with a
poster_media_id). Send alt_text with it. The same bytes again answer the same asset.
curl -sS https://api.tuppence.ai/v1/media \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d "{\"data\": \"$(base64 < candle.jpg)\", \"alt_text\": \"A sea salt candle in a blue tin\"}"import { readFile } from "node:fs/promises";
const photo = await tuppence.request("POST", "/v1/media", { body: { data: (await readFile("candle.jpg")).toString("base64"), alt_text: "A sea salt candle in a blue tin" },});import base64
with open("candle.jpg", "rb") as f: photo = tuppence.request("POST", "/v1/media", body={ "data": base64.b64encode(f.read()).decode(), "alt_text": "A sea salt candle in a blue tin", })POST /v1/media/{id} changes alt_text and focal_point (where crops keep the subject). A
photo your live site uses can’t be deleted.
2. Set its web fields
Section titled “2. Set its web fields”POST /v1/products/{id} takes the web fields beside the till’s. Only what you send changes.
| Field | What |
|---|---|
media | Its photos and videos (med_…) in order. The first is the main one |
web_title, web_description | Its name and description online, if not the till’s |
slug, web_path | Its address, /products/<slug>, and an optional path of its own |
brand, product_type, attributes | For search and feeds |
weight_grams, dimensions_mm, hs_code, origin_country | For delivery and customs |
gpsr | The manufacturer and warnings, for EU sales |
seo | {title, description} for search results |
inventory_policy | At zero stock: deny (the default), continue or preorder with preorder_ships_on |
compare_at_amount | A “was” price. Accepted only after 28 days in a row at that price within six months |
3. Publish it online
Section titled “3. Publish it online”curl -sS https://api.tuppence.ai/v1/products/prod_…/publish_online \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY"const product = await tuppence.request("POST", `/v1/products/${id}/publish_online`);product = tuppence.request("POST", f"/v1/products/{product_id}/publish_online")A product that isn’t ready is refused with what it still needs and a fix_url to the field.
GET /v1/products/{id}/online_checklist lists all of it first:
| Needs | Why |
|---|---|
status: active | A draft is sold nowhere |
| A fixed price | A price typed at the till can’t be shown online |
A photo, processed, with alt_text | Every picture online needs a description |
| A passed claims check | A sentence its facts don’t back is named, with where to add a fact |
POST /v1/products/{id}/unpublish_online takes it out of the store. It stays on sale at the till.
4. Group products into collections
Section titled “4. Group products into collections”A collection is manual (products you pick, in your order) or rule (every product a rule
matches, read from the catalogue each time).
curl -sS https://api.tuppence.ai/v1/collections \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"title": "Gifts under £20", "kind": "rule", "published": true, "rules": {"match": "all", "conditions": [{"field": "price", "op": "lt", "value": 2000}]}}'curl -sS https://api.tuppence.ai/v1/collections \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"title": "Gifts under $20", "kind": "rule", "published": true, "rules": {"match": "all", "conditions": [{"field": "price", "op": "lt", "value": 2000}]}}'curl -sS https://api.tuppence.ai/v1/collections \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{"title": "Gifts under €20", "kind": "rule", "published": true, "rules": {"match": "all", "conditions": [{"field": "price", "op": "lt", "value": 2000}]}}'const gifts = await tuppence.request("POST", "/v1/collections", { body: { title: "Gifts under £20", kind: "rule", published: true, rules: { match: "all", conditions: [{ field: "price", op: "lt", value: 2000 }] }, },});const gifts = await tuppence.request("POST", "/v1/collections", { body: { title: "Gifts under $20", kind: "rule", published: true, rules: { match: "all", conditions: [{ field: "price", op: "lt", value: 2000 }] }, },});const gifts = await tuppence.request("POST", "/v1/collections", { body: { title: "Gifts under €20", kind: "rule", published: true, rules: { match: "all", conditions: [{ field: "price", op: "lt", value: 2000 }] }, },});gifts = tuppence.request("POST", "/v1/collections", body={ "title": "Gifts under £20", "kind": "rule", "published": True, "rules": {"match": "all", "conditions": [{"field": "price", "op": "lt", "value": 2000}]},})gifts = tuppence.request("POST", "/v1/collections", body={ "title": "Gifts under $20", "kind": "rule", "published": True, "rules": {"match": "all", "conditions": [{"field": "price", "op": "lt", "value": 2000}]},})gifts = tuppence.request("POST", "/v1/collections", body={ "title": "Gifts under €20", "kind": "rule", "published": True, "rules": {"match": "all", "conditions": [{"field": "price", "op": "lt", "value": 2000}]},})It is at /collections/<slug>. GET /v1/collections/{id}/products?channel=online lists what
shoppers see in it.
Agents and the assistant
Section titled “Agents and the assistant”Over MCP and in the Tuppence Assistant: update_product_web_fields,
publish_product_online, unpublish_product_online, list_media, update_media,
list_collections, create_collection and update_collection. Prices change only through
update_product, and the assistant always asks before a price changes.
Events
Section titled “Events”| Event | When |
|---|---|
store.product_published | A product went online |
store.product_unpublished | A product came out of the store |
store.collection_created, store.collection_updated, store.collection_deleted | A collection changed |