Skip to content

Build a storefront

An end-to-end walkthrough of building your own e-commerce frontend on the Storefront API — from rendering the shop to a completed order. Every endpoint shown here is documented in full (schemas, parameters, live playground) in the API reference.

Before you start

You need your business API key (sb_live_*), sent as X-Periscale-Key on every request. That single header identifies the store — there is nothing else to configure. Keep it server-side; see Authentication.

Build against the sandbox first, then change one constant to go live:

js
const API = process.env.NODE_ENV === "production"
  ? "https://storefront.periscale.app"
  : "https://dev-storefront.periscale.app";

Proxy pattern

In a browser app, don't call the API directly from the client — the key would leak. Route calls through your own backend (a Next.js route handler, an Express route, …) that injects X-Periscale-Key and, when the shopper is logged in, forwards their Authorization header.

The snippets below assume two small helpers: api() for key-only calls and shopperApi() for calls that also carry the shopper's Bearer token.

1. Load the store configuration

Everything needed to render the shell of the shop — identity, theme, hero, navigation, footer, payment methods, announcement bar — comes from one call:

bash
curl https://dev-storefront.periscale.app/api/v1/business/website/config/ \
  -H "X-Periscale-Key: sb_live_..."
js
const res = await fetch(`${API}/api/v1/business/website/config/`, {
  headers: { "X-Periscale-Key": process.env.PERISCALE_KEY },
});
const { data: config } = await res.json();

The response is cached server-side, so call it freely on every page render. → Reference: Get store config

2. Render the catalog

List products, fetch one by slug, and browse categories. Lists come back as { items, next_cursor, has_more } — see Pagination & fields:

bash
curl "https://dev-storefront.periscale.app/api/v1/business/website/products/?sort=newest&limit=24" \
  -H "X-Periscale-Key: sb_live_..."

curl https://dev-storefront.periscale.app/api/v1/business/website/products/classic-tee/ \
  -H "X-Periscale-Key: sb_live_..."
js
const { items, next_cursor, has_more } =
  (await api("/business/website/products/?sort=newest&limit=24")).data;

const product = (await api("/business/website/products/classic-tee/")).data;
const categories = (await api("/business/website/categories/")).data.items;

Sort with ?sort=newest (default), popular, price_asc, price_desc. For a listing grid you rarely need the full product object; ask for just the card fields and the response gets both smaller and faster to produce:

js
await api("/business/website/products/?fields=id,slug,name,price,images");

For the home page, dedicated endpoints serve curated sections: top-products/, new-arrivals/, flash-deals/, bundle-products/, featured-reviews/. → Reference: Catalog

3. Register and log in shoppers

Shopper accounts use the customer auth endpoints, with the same API key as every other call:

bash
curl -X POST https://dev-storefront.periscale.app/api/v1/customer/auth/login/ \
  -H "X-Periscale-Key: sb_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "email": "shopper@example.com", "password": "…" }'
js
const { data } = await fetch(`${API}/api/v1/customer/auth/login/`, {
  method: "POST",
  headers: {
    "X-Periscale-Key": process.env.PERISCALE_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email, password }),
}).then((r) => r.json());
// data.access_token, data.refresh_token, data.customer

Store data.access_token and send it as Authorization: Bearer <token> on all shopper actions below; renew it with POST /api/v1/customer/auth/refresh/ (body: { "refresh_token": "…" }). → Reference: Shopper authentication

4. Cart

All cart calls need both headers — the key and the shopper's Bearer token:

bash
curl -X POST https://dev-storefront.periscale.app/api/v1/business/website/cart/items/ \
  -H "X-Periscale-Key: sb_live_..." \
  -H "Authorization: Bearer <access>" \
  -H "Content-Type: application/json" \
  -d '{ "product_id": 42, "quantity": 2 }'
js
await shopperApi("/business/website/cart/items/", {
  method: "POST",
  body: JSON.stringify({ product_id: 42, quantity: 2 }),
});
const cart = await shopperApi("/business/website/cart/");

Use cart/summary/ for a lightweight totals-only payload (badge counters), PATCH cart/items/{item_id}/ to change quantities, and DELETE cart/clear/ to empty the cart. → Reference: Cart

5. Checkout

  1. (Optional) validate a coupon: POST coupon/validate/
  2. (Optional, for cash on delivery) run the risk check: POST cod-risk-check/
  3. Create the order from the cart:
bash
curl -X POST https://dev-storefront.periscale.app/api/v1/business/website/order/ \
  -H "X-Periscale-Key: sb_live_..." \
  -H "Authorization: Bearer <access>" \
  -H "Content-Type: application/json" \
  -d '{ "payment_method": "cod", "shipping_address": { } }'
js
const order = await shopperApi("/business/website/order/", {
  method: "POST",
  body: JSON.stringify(checkoutPayload),
});

For gateway payments (bKash/Nagad), follow the order with POST payment/initiate/ (returns a redirect URL) and confirm with POST payment/verify/ after the shopper returns. → Reference: Checkout & payment

Test checkout in the sandbox

Sandbox orders are real rows in the sandbox store — safe to create, cancel and refund freely. Point at production only once the flow is settled.

6. Post-purchase account area

Everything a typical "My account" section needs, all shopper-token protected:

FeatureEndpoints
Order history & trackingGET orders/, GET orders/{order_id}/requests/
Cancellations & refundsPOST orders/cancel/, POST orders/refund/, GET orders/refunds/
WishlistGET/POST/DELETE wishlist/
AddressesGET/POST addresses/, PUT/DELETE addresses/{address_id}/
ReviewsPOST reviews/, GET my-reviews/
Support ticketsGET/POST tickets/, GET tickets/{ticket_id}/, POST tickets/{ticket_id}/messages/

Reference: Orders · Account

7. Content pages

Render store pages (pages/, pages/{slug}/) and the blog (blog/, blog/{slug}/), and wire the newsletter (POST subscribe/) and contact form (POST contact/) — all with just the API key. → Reference: Content

Going live

  1. Swap the base URL to https://storefront.periscale.app.
  2. Swap in the production sb_live_* key — sandbox keys return 401 against production.
  3. Re-check anything that hardcoded IDs or slugs: sandbox and production are separate stores with separate data.

Analytics

For visitor analytics and event tracking, drop in the Analytics tag — it handles identity and event ingestion automatically; there's nothing to build against here.

Endpoint groups

GroupAuthReference
Store config & catalogKeyBrowse
Shopper authenticationKeyBrowse
CartKey + BearerBrowse
Checkout & paymentKey + BearerBrowse
OrdersKey + BearerBrowse
AccountKey + BearerBrowse
ContentKeyBrowse

© Periscale