API reference
The Shoppi SDK, the JSON API and TagCodes: everything a partner needs to build on a Shoppi store.
How it works
Every store on Shoppi Cloud can be extended with the Shoppi SDK, a JavaScript library served by the platform on every Shoppi-hosted domain, and with TagCodes, server-side placeholders the page engine resolves before the page reaches the browser. Both talk to the same JSON API, which is also available directly.
- No API keys in the browser. The store is identified by the domain the request is made from, so the SDK works on the store's own domain without secrets in your code.
- Payments go through Stripe. Checkout sessions are created on the merchant's own Stripe account (Stripe Connect); Shoppi never holds the merchant's customers' money. Refunds also run entirely through Stripe.
- Two databases. The catalog is read from the Shoppi database the merchant manages from the app; each store domain also has its own database. You choose which one each API reads (see Catalog). Customer accounts always live in the store domain's own database, separate from other stores.
- Current SDK version: 2.9.0. Responses use one envelope:
{"result": "ok" | "ko", "feedback": …}.
Setup
Include the SDK once, in the page <head>. It creates a global window.Shoppi object on load.
<script>
window.ShoppiConfig = {
pageId: "%_ID%", // store id; the page engine fills it in (use %_ID% inside JS, not {id})
currency: "EUR", // default currency for cart and checkout
defaultLocale: "it", // language used when the URL has no /xx/ prefix
baseUrl: "", // leave empty: API calls go to the same origin
sources: "shoppi" // catalog database: "shoppi" (default) or "local"
};
</script>
<script src="/shoppi/shoppi-sdk.js?v=2.9.0"></script>
All settings are optional: without ShoppiConfig the SDK resolves the store from the domain and uses EUR. Bump the ?v= parameter when you want browsers to fetch a newer SDK.
Store and content — Shoppi.site
| Method | Returns |
|---|---|
Shoppi.site.getInfo() |
Store profile: name, logo, cover, description, category, city, country, language. |
Shoppi.site.getPage(slug) |
A content page by its slug: title, HTML content and keywords, or null. |
Shoppi.site.getPages() |
The store sections: content pages and the categories products belong to. |
Shoppi.site.getProducts(options) |
Same as Shoppi.catalog.products(). |
Shoppi.site.getProduct(idOrSlug) |
Same as Shoppi.catalog.product(). |
Shoppi.site.getCategories() |
Same as Shoppi.catalog.categories(). |
Shoppi.site.search(q) / getSuggestions(q) |
Same as Shoppi.catalog.search() and suggestions(). |
const info = await Shoppi.site.getInfo();
document.getElementById('store-name').textContent = info.title;
Catalog — Shoppi.catalog
Products, categories, photos and search: the data the merchant manages in the Shoppi app, ready to build a storefront. Every item comes back in the same shape, whatever its type:
{
id: 9610389, type: 'products', // products | services | files | offers
slug: 'curtirisorisobasmati2×500g',
title: 'Curtiriso Riso Basmati 2 × 500 g',
description: '', content: 'Basmati', // content may contain HTML
price: 4.79, currency: 'EUR', price_format: '4,79 €',
photo: 'https://storage.shoppi.app/…', photo_hires: '…', photos: ['…'],
quantity: 5, in_stock: true, ship_cost: 0, code: 'CX-DIS-000001',
category: 'Dispensa', keywords: ['dispensa'], link: '…', date: 1791312283
}
| Method | Returns |
|---|---|
Shoppi.catalog.products(options) |
Returns { items, count, limit, skip, hasMore }. Options: type (products, services, files, offers, all), limit (24), skip, sort (newest, updated, popular, price_asc, price_desc, title), category (id, slug or title), q, ids, inStock. |
Shoppi.catalog.product(idOrSlug, { type }) |
One item, or null. An item of another store is never returned. |
Shoppi.catalog.categories({ type }) |
sections (default: the categories products are attached to), albums or tree. |
Shoppi.catalog.category(ref) |
One category by id, slug or title. |
photos(), pages(), page(slug), news(), videos() |
The store content. |
Shoppi.catalog.search(q, { type, limit, minPrice, maxPrice }) |
Returns { items, count, suggestions, did_you_mean }. |
Shoppi.catalog.suggestions(q) |
Titles for an autocomplete. |
Shoppi.catalog.info() |
Store profile. |
const page = await Shoppi.catalog.products({ limit: 12 });
const cats = await Shoppi.catalog.categories(); // [{ id: 600, slug: 'dispensa', title: 'Dispensa' }, …]
const food = await Shoppi.catalog.products({ category: 'dispensa' });
const one = await Shoppi.catalog.product(9610389); // or by slug
const hits = await Shoppi.catalog.search('riso'); // { items, count, suggestions }
Which database: per API, per call
'shoppi' (default) reads the Shoppi database, where the merchant manages products from the app. 'local' reads the store domain's own database, empty until you write to it. Choose per API in ShoppiConfig.sources, at runtime with Shoppi.catalog.use(), or for a single call with { source }. A source can also be the base URL of another host or a function returning your own data. Cart, checkout, customer accounts and email always run on the store's own domain.
window.ShoppiConfig = {
pageId: "%_ID%",
sources: { products: 'shoppi', categories: 'local' } // or one value: 'local'
};
Shoppi.catalog.use('search', 'local'); // at runtime, one API
await Shoppi.catalog.products({ limit: 12 }, { source: 'local' }); // for one call
Rendering and events
Shoppi.render(target, items, template) fills an element with a list: the template is a function, a Handlebars string, or nothing for the built-in card with its add-to-cart button. Elements with data-shoppi-list fill themselves when the page loads; their other data-shoppi-* attributes are the options of the matching method. Events on document: shoppi:cart:added, shoppi:cart:updated, shoppi:checkout:started, shoppi:rendered.
const r = await Shoppi.catalog.products({ limit: 12 });
Shoppi.render('grid', r); // built-in card with add-to-cart button
Shoppi.render('grid', r, p => `<h3>${p.title}</h3><p>${p.price_format}</p>`);
<div data-shoppi-list="products" data-shoppi-limit="12" data-shoppi-category="dispensa">
<template><div class="card"><img src="{{photo}}"><h3>{{title}}</h3>
<button data-shoppi-add="{{id}}" data-shoppi-price="{{price}}"
data-shoppi-title="{{title}}" data-shoppi-image="{{photo}}">Add</button></div></template>
</div>
document.addEventListener('shoppi:cart:added', e => console.log(e.detail.title));
Cart — Shoppi.cart
The cart lives in the browser (localStorage) and survives reloads. Prices and titles you pass travel to checkout, so always set them.
| Call | What it does |
|---|---|
Shoppi.cart.add(id, qty, { price, title, image }) |
Adds a product (qty defaults to 1). |
Shoppi.cart.updateQty(id, qty) |
Sets the quantity; 0 removes the line. |
Shoppi.cart.remove(id) |
Removes a line. |
Shoppi.cart.clear() |
Empties the cart. |
Shoppi.cart.setCountry(code) |
Sets the shipping country (ISO code, e.g. 'IT'). |
Shoppi.cart.data |
Current state: { items, count, total, country }. |
Without JavaScript, using HTML attributes:
<button data-shoppi-add="10034"
data-shoppi-price="29.90"
data-shoppi-title="Leather wallet"
data-shoppi-image="/img/wallet.jpg">Add to cart</button>
<span class="shoppi-cart-count"></span> <!-- item count, updated automatically -->
<span class="shoppi-cart-total"></span> <!-- total, formatted in the store currency -->
<select data-shoppi-country> <!-- shipping country -->
<option value="IT">Italia</option>
<option value="DE">Deutschland</option>
</select>
<button data-shoppi-checkout>Checkout</button>
Checkout — Shoppi.checkout
| Method | What it does |
|---|---|
Shoppi.checkout.start() |
Creates a Stripe Checkout session for the current cart on the merchant's Stripe account and redirects the customer to it. |
Shoppi.checkout.startDirect(productId, metadata) |
One-click purchase of a single product, without the cart (services, bookings, downloads). |
Shoppi.checkout.createOnboardingLink() |
Returns the Stripe onboarding URL for the store, to connect or complete its Stripe account. |
document.querySelector('#buy').addEventListener('click', () => Shoppi.checkout.start());
// Stripe onboarding for the merchant
const url = await Shoppi.checkout.createOnboardingLink();
window.location.href = url;
Any button with data-shoppi-checkout does the same as Shoppi.checkout.start(). Payment fees follow the store's plan: Stripe processing fees plus the Shoppi Pay app fee of 0.4% + 1 minor currency unit per payment. Refunds, full or partial, online or at the POS, are issued through Stripe too.
Customer accounts — Shoppi.user
Accounts for the store's own customers, stored in the store's database. After login the session token is kept in the browser and sent with every SDK request in the X-User-Token header. Accounts are tied to the store domain: two stores never see each other's customers.
| Method | What it does |
|---|---|
Shoppi.user.register(email, password, firstName, lastName) |
Creates an account and logs in. |
Shoppi.user.login(email, password) |
Logs in. |
Shoppi.user.getProfile() |
Profile of the logged-in customer (null when logged out). |
Shoppi.user.updateProfile(data) |
Updates profile fields, e.g. { phone }. |
Shoppi.user.saveAddress(type, address) |
Saves an address; type is e.g. 'shipping'. |
Shoppi.user.logout() |
Clears the session and reloads the page. |
Navigation and translations — Shoppi.router, Shoppi.lang
URLs follow /<language>/<page>, e.g. /it/contatti. Shoppi.router.navigate(slug, locale) loads a page without a full reload and renders it in <main>; links with data-shoppi-link do the same.
<a href="/it/contatti" data-shoppi-link="contatti">Contatti</a>
Shoppi.lang.load(locale) loads the store's translations; Shoppi.lang.t(key, vars) returns a string and replaces placeholders written as %_NAME%.
Local directory — Shoppi.local
| Method | Returns |
|---|---|
Shoppi.local.getCities() |
Cities available in the local directory. |
Shoppi.local.getBusinesses(cityId, categoryId) |
Businesses in a city, optionally filtered by category. |
Email — Shoppi.mailer
Sends a transactional email from the store, e.g. a contact form. Limited to 10 emails per day per device.
await Shoppi.mailer.send({
to: '[email protected]',
subject: 'New request from the website',
body: '<p>Hello!</p>',
replyTo: '[email protected]' // optional
});
Shoppi.mailer.remaining(); // emails left today on this device
JSON API
Every SDK method is a call to the JSON API, which you can use directly with Shoppi.call(endpoint, params, method) or plain fetch: the pattern is /api/<module>/<method>/json, on the store's own domain.
| Endpoint | Purpose |
|---|---|
GET /api/website/info/json |
Store profile |
GET /api/website/section/json?clear_title=<slug> |
Content page |
POST /api/checkout/createStripeSession/json |
Stripe Checkout session from a cart |
POST /api/checkout/startDirect/json |
Direct purchase of one product |
GET /api/checkout/createOnboardingLink/json |
Stripe onboarding link |
POST /api/frontend_user/register/json |
Customer sign-up |
POST /api/frontend_user/login/json |
Customer login |
GET /api/local/getCities/json |
Local directory: cities |
GET /api/website/products/json |
Products (also listServices, listFiles, listOffers) |
GET /api/website/product/json?id= |
One product, by id or clear_title
|
GET /api/website/sections/json |
Sections and categories |
GET /api/website/listSells/json?parent= |
Items attached to a section |
GET /api/search/results/json?query= |
Search |
GET /api/search/suggestions/json?query= |
Autocomplete |
const res = await fetch('/api/website/info/json', { credentials: 'include' });
const data = await res.json(); // { "result": "ok", ... } or { "result": "ko", "feedback": "reason" }
Catalog endpoints answer on the store domain (its own database) and on https://shoppi.app (the Shoppi database). For the 'shoppi' source the SDK calls shoppi.app with the store id: an id that does not belong to the calling domain is refused. Shoppi.call(endpoint, params, method, { base }) picks the host.
Shoppi.call() returns the useful part of an ok response and throws an Error with the feedback message on ko.
TagCodes (server-side)
TagCodes are placeholders in the store's HTML templates, resolved on the server before the page is sent, so the content is in the HTML that search engines see.
| TagCode | Output |
|---|---|
{css} / {js}
|
Stylesheets and scripts of the page and its plugins |
{page_title}, {page_description}
|
SEO title and description |
{page_name}, {page_logo}
|
Store name and logo |
{locale}, {currency}, {country}
|
Active language, currency, visitor country |
{title}, {content}, {photo}, {price}, {link}
|
Fields of the current product or article |
{cfield:title}, {cfield:description}, {cfield:cover}
|
Store profile fields |
{cart:act=drawer} |
Cart side drawer |
{search:nout=true} |
Search, initialised without default output |
{cookieconsent} |
Cookie consent banner |
{offers:act=seo} |
JSON-LD structured data for products |
Inside a JavaScript object literal use %_ID% for the store id: braces there are read as a TagCode.
Files and data access
Each store's files are reachable over WebDAV (also through the Shoppi Go desktop app), and its data stays exportable: standard HTML templates, the store's own database, and the JSON API above. Access credentials are in the store's account panel.
Questions or a use case not covered here? Book a call with the partner team, or go back to the partner kit.