Headless Storefront API
On this page
The Storefront API is for developers who want their own front end, for example a Next.js site or a phone app, while Kartlo.App keeps the catalog, the cart and the checkout. It reads only what is published on your storefront.
Storefront access tokens#
- Go to SettingsDevelopers and click Create storefront token.
- Name it and list the websites allowed to call it from a browser, one per line, for example
https://www.example.com. - Copy the token. Kartlo.App shows it once.
Tokens start with kartlo_pk_. They can only read published data and build carts, so they are safe in browser code. They cannot read orders, customers or stock. Send the token as X-Kartlo-Storefront-Access-Token or as a Bearer token.
Allowed origins#
A browser call is answered only when its site is on the token's list. Calls from a server or a phone app send no origin and are always answered. Use http:// only for localhost.
Endpoints#
All paths start with https://kartlo.app/api/storefront/v1.
| Method and path | Returns |
|---|---|
| GET /shop | Store name, currency, language, contact details, payment methods, policy pages and theme colors and fonts. |
| GET /products | Products. Filters: q, vendor, price_min, price_max, available. Sort: newest, price_asc, price_desc, title. limit 1 to 48, page. |
| GET /products/{handle} | One product with images, options, variants and buyer inputs. |
| GET /collections | Collections with their product counts. |
| GET /collections/{handle} | One collection, its products, vendor and price facets. |
| GET /menus | The navigation menu and published pages. |
| GET /pages/{handle} | One page with its content. |
| GET /blogs | Blog posts, when the Blogs app is on. Filters: category, tag, q. |
| GET /blogs/{handle} | One post with the previous, next and related posts. |
| POST /carts | A new cart, optionally with lines. |
| GET /carts/{cartId} | The cart with lines, subtotal and discounts. |
| POST /carts/{cartId}/lines | Adds a variant: variant_id, quantity, properties. |
| PATCH /carts/{cartId}/lines/{lineId} | Changes the quantity. 0 removes the line. |
| DELETE /carts/{cartId}/lines/{lineId} | Removes the line. |
| POST /carts/{cartId}/discount | Applies a discount code. |
| DELETE /carts/{cartId}/discount | Removes the discount code. |
| POST /carts/{cartId}/checkout | A checkout_url that opens your hosted checkout with this cart. |
const API = 'https://kartlo.app/api/storefront/v1';
const TOKEN = 'kartlo_pk_...';
const sf = (path, init = {}) =>
fetch(API + path, {
...init,
headers: { 'X-Kartlo-Storefront-Access-Token': TOKEN, 'Content-Type': 'application/json', ...init.headers },
}).then((r) => r.json());const { shop } = await sf('/shop');
const { products, page_count } = await sf('/products?sort=price_asc&available=true&limit=12');
const { product } = await sf('/products/oud-oil');
const { collection, products: inCollection } = await sf('/collections/summer?page_size=24');
const { menu } = await sf('/menus');
const { page } = await sf('/pages/about-us');
const { posts } = await sf('/blogs?category=news');Cart and checkout#
The cart is kept by Kartlo.App. Save its id in your site, for example in local storage. Delivery and tax are worked out at checkout. The checkout URL opens your store's own checkout, so payments, delivery options and order emails work as usual.
let { cart } = await sf('/carts', { method: 'POST', body: JSON.stringify({}) });
({ cart } = await sf(`/carts/${cart.id}/lines`, {
method: 'POST',
body: JSON.stringify({ variant_id: product.default_variant_id, quantity: 1 }),
}));
({ cart } = await sf(`/carts/${cart.id}/lines/${cart.lines[0].id}`, { method: 'PATCH', body: JSON.stringify({ quantity: 2 }) }));
({ cart } = await sf(`/carts/${cart.id}/discount`, { method: 'POST', body: JSON.stringify({ code: 'SAVE10' }) }));
const { checkout_url } = await sf(`/carts/${cart.id}/checkout`, { method: 'POST' });
window.location.href = checkout_url;Errors, limits and caching#
An error answer has error.code and error.message.
| Status | Meaning |
|---|---|
| 401 | The token is missing, wrong or revoked. |
| 402 | The store is not on the Pro plan or above. |
| 403 | The browser site is not on the token’s list, or the store is password protected or closed. |
| 404 | No such product, collection, page, post or cart in this store. |
| 422 | The cart action was refused, for example out of stock or a code that does not apply. The message says why. |
| 429 | Too many requests. Each token can make 3,000 requests a minute, and each visitor 300. |
Catalog answers are cached for up to a minute, so changes in your dashboard show within about a minute. Searches and carts are never cached. Filtered and searched product lists cover your newest 250 products.