Build a complete storefront
A worked, end-to-end walkthrough: boot → home → listing → product → cart →
checkout → order → account. Every step shows the real request and the response
fields you render. Base URL is the store domain: https://<store-domain>/api.
0. Boot — load store config
One call gives you currency, branding, enabled features and the default store.
curl https://<store-domain>/api/organization
{
"data": {
"name": "Tamimi",
"logo": "https://cdn/.../logo.png",
"currency": {
"name": "Saudi Riyal",
"symbol": "SR"
},
"theme": {
"name": "Fashion",
"slug": "theme-fashion"
},
"defaultStoreId": 7,
"languages": [
"en",
"ar"
],
"config": {
"order": {
"orderTypes": [
"DELIVERY",
"PICKUP"
],
"paymentMethods": [
"CASH",
"ONLINE"
],
"minimumOrderValue": 50
},
"website": {
"primaryColour": "#0a7",
"authProviders": [
"OTP",
"PASSWORD"
],
"isCustomerWishlistEnabled": true
}
}
}
}
Use currency.symbol for prices, config.website flags to toggle features
(wishlist, ratings, slots…), and config.order.paymentMethods at checkout.
1. Home — render the layout
curl https://<store-domain>/api/layout/home
layouts is an ordered array of sections. Render each from its value (see
the layout system):
{
"data": {
"layouts": [
{
"name": "ImageSlideShow",
"status": "ENABLED",
"value": {
"images": [
{
"url": "https://…/banner.jpg",
"link": "/category/fruits"
}
]
}
},
{
"name": "ProductCollection",
"status": "ENABLED",
"data": {
"title": "Best Sellers"
},
"value": {
"collection": {
"count": 240,
"limit": 20,
"product": []
}
}
}
]
}
}
Map section.name → a component; feed it section.value.
2. Listing (PLP) — products in a category
Either render the category page layout (/api/layout/category?url={slug}) or query
products directly:
curl "https://<store-domain>/api/product?categoryId=1201&page=1&limit=20"
{
"data": {
"count": 240,
"limit": 20,
"offset": 0,
"product": [
{
"id": 10231,
"name": "Tata Salt 1kg",
"slug": "tata-salt-1kg",
"images": [
"https://…/p1.jpg"
],
"storeSpecificData": [
{
"storeId": "<store-id>",
"mrp": "28.00",
"discount": "10",
"stock": 240
}
],
"averageRating": 4.4,
"hasVariants": 0
}
]
}
}
Computing the price to show: mrp is a string, and
discount is a percentage string — both on storeSpecificData for the store
you're selling in:
sellingPrice = mrp − (mrp × discount / 100)
// 28.00 with discount "10" → 25.20
Out of stock when stock === 0 and unlimitedStock !== true.
3. Product detail (PDP)
curl "https://<store-domain>/api/layout/product?url=tata-salt-1kg"
The full product is on page.entity (with variants, each variant carrying its
own storeSpecificData and images); the page's sections come in layouts. For
a product with variants (hasVariants: 1), let the shopper pick a variant and
price from that variant's storeSpecificData.
4. Add to cart
Items are { id: variantId, q: quantity, t: timestampMs }. q: "0" removes.
curl -X POST https://<store-domain>/api/cart \
-H "Content-Type: application/json" \
-d '{ "cart": { "items": [ { "id": "88120", "q": "2", "t": 1750000000000 } ] }, "orderType": "DELIVERY", "storeId": "<store-id>" }'
5. Render the cart
curl https://<store-domain>/api/cart
{
"data": {
"items": [
{
"id": "88120",
"q": 2,
"mrp": 28,
"discount": 10,
"product": {
"name": "Tata Salt 1kg"
}
}
],
"youPay": 50.4,
"savings": 5.6,
"amounts": {
"orderAmount": 50.4,
"totalMRP": 56,
"totalDiscount": 5.6,
"shippingAmount": 0,
"totalCouponDiscount": 0
},
"isServiceable": true,
"storeId": "<store-id>",
"orderType": "DELIVERY"
}
}
Render line items from items, the summary from amounts, and the headline total
from youPay / savings.
6. Sign the shopper in (if required)
Guest checkout is allowed, but for saved addresses/orders, log in:
curl -X POST https://<store-domain>/api/login \
-H "Content-Type: application/json" -d '{ "username": "asha@example.com", "password": "••••••" }'
# → { "data": { "accessToken": "eyJ…", "customer": { "id": 7781 } } }
Send Authorization: Bearer <accessToken> on the calls below. (OTP flow:
POST /otp → POST /customer-verification.)
7. Checkout — totals, slots, payment options
curl -X POST https://<store-domain>/api/checkout \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{ "addressId": "addr-123", "preferredSlotId": 31, "paymentMethod": "ONLINE", "couponCodes": ["SAVE20"] }'
{
"data": {
"cart": {
"youPay": 70.4,
"amounts": {
"shippingAmount": 20,
"totalCouponDiscount": 0
}
},
"slot": {
"2026-06-23": [
{
"id": 31,
"text": "9–10 AM",
"available": true,
"charge": 0
}
]
},
"paymentMethods": [
"CASH",
"ONLINE"
],
"couponDiscounts": [
{
"coupon": {
"couponCode": "SAVE20"
},
"applied": false,
"error": "Min order not met"
}
]
}
}
Use slot (keyed by date) to render the slot picker, paymentMethods for the
payment choices, and re-read cart.amounts for the final figure.
8. Place the order & pay
curl -X POST https://<store-domain>/api/order -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" -d '{ "addressId": "addr-123", "preferredSlotId": 31, "paymentMethod": "ONLINE" }'
For online payment, initiate it and redirect the shopper using the returned
redirectUrl / payment key (GET /api/payment-key):
curl -X POST https://<store-domain>/api/order/55012/payment -H "Authorization: Bearer $TOKEN"
9. Account & orders
curl https://<store-domain>/api/me -H "Authorization: Bearer $TOKEN" # profile
curl https://<store-domain>/api/order -H "Authorization: Bearer $TOKEN" # order history
curl "https://<store-domain>/api/track?orderId=ORD-55012" # delivery tracking
That's a complete store. From here add wishlist, ratings, blogs and notifications from the Storefront API reference.
Using the SDK instead
The @zopping/ecom SDK + theme path gives you these flows as hooks
(useLayout, useCart, useCheckout) — see Build a storefront.