Storefront API

The public API behind every Joomni storefront. Use it to build your own website or app on top of a Joomni store — no API key needed.

Overview

What it is

Every Joomni storefront reads its catalogue and places its orders through these endpoints. They return only what a shopper may see, so they need no key: you pick the store with one header. The Next.js Commerce starter is built entirely on them.

Who it is for
Storefront API:Your own website or app, used by shoppers
REST API (v1):Your own systems: ERP, courier, spreadsheet
Authentication
Storefront API:None. The x-subdomain header picks the store
REST API (v1):Secret jk_live_… key with scopes
What it can do
Storefront API:Read the published catalogue, quote shipping, check a code, place a guest order, track it
REST API (v1):Read and change products, orders, stock and leads
Prices on orders
Storefront API:Always taken from your catalogue
REST API (v1):Recorded as you send them
Base URL
Storefront API:https://api.joomni.com/storefront
REST API (v1):https://api.joomni.com/api/v1

Safe by design

A storefront order is a request, not a record. Joomni reads prices from your catalogue, re-quotes shipping, re-checks discount codes and takes the stock itself. Totals in the request body are ignored, so a visitor can't change what they pay.

Quick start

Your first call

Use your store's subdomain — mystore for mystore.joomni.com.

List four productsbash
curl https://api.joomni.com/storefront/products?limit=4 \
-H "x-subdomain: mystore"
  1. 1

    Pick the store

    Send x-subdomain: mystore on every request. An unknown or inactive store answers 404.
  2. 2

    Read the envelope

    Most endpoints wrap their answer as { data }. The shipping quote and discount check answer unwrapped.
  3. 3

    Call from a server

    Fetch from your server (or server components) and cache catalogue reads; they change only when you edit the store.

Endpoints

Store and catalogue

Only active products and published collections are returned. Drafts and archived products answer 404.

Store

Name, currency, SEO, and which payment methods checkout offers.

  • GET/storefront/store

    storeName, currency, locale, logo, seoTitle, seoDescription; paymentSettings.cod and paymentSettings.customQr (enabled, fee, instructions); customerAccounts.mode (OPTIONAL, REQUIRED or GUEST_ONLY).

Products and collections

Paged lists and single records, with variants.

  • GET/storefront/products

    page, limit, search, collectionId. Answers { data: Product[], meta }, newest first, each product with its variants.

  • GET/storefront/products/:handle

    One product by handle (or id), with variants and the collections it belongs to.

  • GET/storefront/collections

    page, limit. Published collections.

  • GET/storefront/collections/:handle

    { collection, products }. For a manual collection, page its products through /storefront/products?collectionId= instead; that list includes variants. Automatic (tag-rule) collections use the products here.

Menus

The header and footer menus you build under Website › Navigation.

  • GET/storefront/navigation

    Every menu by handle; stores start with main-menu and footer. Each item has label, url (already resolved to the current product or collection handle) and children.

  • GET/storefront/navigation/:handle

    One menu.

Endpoints

Checkout

Quote, check, place. Nothing you compute here is trusted: the same calculations run again when the order is placed.

Shipping and discounts

For the order summary while the shopper fills in checkout.

  • POST/api/shipping/calculate

    Note the /api prefix. Body: storeId (from /storefront/store), items (productId, quantity, price) and address (province, city, country, zip). Answers { total }, or { unavailable: true } when no shipping zone covers the address.

  • POST/storefront/discounts/validate

    Body: code and cart (subtotal, items, shipping). Answers { valid, discount, amount }, or 400 with the reason (expired, minimum not met, used up).

Orders

Place a guest order, then follow it with its tracking token.

  • POST/storefront/orders

    Places an order, takes the stock and emails the buyer and you. Answers the order with its trackingToken. 401 LOGIN_REQUIRED when the store only takes orders from signed-in customers.

  • GET/storefront/orders/track/:token

    Status, items, totals, shipments with tracking numbers, and payment: the Scan & Pay QR and amount due while the order is unpaid (otherwise null).

  • POST/storefront/orders/track/:token/payment

    Scan & Pay: the buyer reports transactionId (and optional payerName). You confirm it in the dashboard. Limited to 5 requests a minute.

Place an orderjavascript
await fetch('https://api.joomni.com/storefront/orders', {
method: 'POST',
headers: { 'x-subdomain': 'mystore', 'Content-Type': 'application/json' },
body: JSON.stringify({
customerEmail: '[email protected]',
customerName: 'Asha Shrestha',
items: [
// price is required by the schema but ignored: Joomni reads the catalogue.
{ productId: 'cmg1prod01', variantId: 'cmg1var01', quantity: , title: 'Lokta Notebook', price: },
],
shippingAddress: {
firstName: 'Asha', lastName: 'Shrestha', phone: '98XXXXXXXX',
address1: 'Jhamsikhel Road', city: 'Lalitpur', province: 'Bagmati', country: 'Nepal',
},
billingAddress: { /* same shape */ },
paymentMethod: 'COD', // or 'CUSTOM_QR' (Scan & Pay)
discountCodes: ['WELCOME10'], // optional
note: 'Please call before delivery',
}),
});

Payments

Taking payment

Storefront orders are paid by the methods your checkout completes today.

COD
When it is accepted:Cash on delivery is on, or no method is on at all
What happens next:The order is placed unpaid. If COD confirmation is on, it waits for your call before dispatch.
CUSTOM_QR
When it is accepted:Scan & Pay is on
What happens next:Show the QR from the tracking endpoint; the buyer pays and reports the transaction ID; you mark the order paid.

Good practice

Rules for a healthy integration

  • Cache catalogue reads and refresh them with product webhooks or a timer.
  • Never show a total as final before the order is placed; the response holds the real one.
  • Treat the trackingToken as the buyer's key to their order: don't index or log it.
  • Handle 404 on a product: it may have been unpublished since your page was cached.
  • Ignore fields you don’t know; new ones may be added at any time.
  • Customer sign-in endpoints exist but aren’t part of this public contract yet.

Want a working example? The Next.js Commerce starter calls every endpoint on this page from src/lib/joomni/storefront.ts.

Need a hand?