# Sepetak > Sepetak is a hosted storefront platform for Indonesian small shops. Each shop has a public storefront API at https://.sepetak.com/api/v1; a merchant's own site (headless) calls it from a domain registered to the shop. No API key. ## Guide - [Access](https://docs.sepetak.com/guide/access.md): No API key. A server may read anything; a browser needs a registered domain and a plan that covers the method. - [Checkout in five calls](https://docs.sepetak.com/guide/checkout.md): Prices are computed server-side; the buyer confirms with a one-time code; track_token is returned once. - [Errors](https://docs.sepetak.com/guide/errors.md): One envelope for every error; message is Indonesian and safe to show. - [Limits and buyer links](https://docs.sepetak.com/guide/limits.md): Code budgets, per-IP limits, idempotent retries, and where buyer emails point. - [Quickstart](https://docs.sepetak.com/guide/quickstart.md): One fetch against the catalogue, pinned to an API version. - [Versions](https://docs.sepetak.com/guide/versions.md): Each API version is a date. Old versions stay served; X-API-Version picks one. ## API reference - [Every operation and field, current version 2026-09-09](https://docs.sepetak.com/api/reference.md) - [OpenAPI 3.1 document](https://sepetak.com/openapi.json): `?version=YYYY-MM-DD` for an older shape; `info.x-versions` lists them ## Other - [Changelog](https://docs.sepetak.com/changelog.md): What each API version changed. Generated from the OpenAPI documents. - [JavaScript client](https://www.npmjs.com/package/@sepetakhq/storefront-kit): the fetch layer the hosted templates use --- # Sepetak storefront API Build your own storefront on a Sepetak shop. Your site calls the same API the hosted templates use. - **Base URL** — `https://.sepetak.com/api/v1` (any host the shop answers on) - **Spec** — [`https://sepetak.com/openapi.json`](https://sepetak.com/openapi.json), OpenAPI 3.1, one document per version (`?version=YYYY-MM-DD`) - **Plan needed** — Tumbuh to read · Skala to check out ([Access](/guide/access)) - **For machines** — [`/llms.txt`](pathname:///llms.txt); every page also exists as `.md` at the same path ## Using AI? Hand it this ## Where to start 1. [Access](/guide/access) — there is no key; the rules are your origin and your plan. 2. [Quickstart](/guide/quickstart) — one `fetch`. 3. [Checkout in five calls](/guide/checkout) — the sequence and the rules the server enforces. 4. [API reference](/api) — every route and field, rendered from the spec. --- {/* GENERATED by scripts/changelog.mjs from specs/*.json. Edit the platform release list, not this file. */} # Changelog Each API version is a date. Pin one with `X-API-Version`; an unpinned request gets the baseline. Old versions stay served. See [Versions](/guide/versions). ## 2026-09-09 (current) - checkout draft requires a buyer phone Reference: [2026-09-09](/api) (pick it in the document switcher) · [openapi.json](https://sepetak.com/openapi.json?version=2026-09-09) ## 2026-09-05 - Baseline. The shape every unpinned request receives; it never changes. Reference: [2026-09-05](/api) (pick it in the document switcher) · [openapi.json](https://sepetak.com/openapi.json?version=2026-09-05) --- # Access There is no API key. The shop is the host you call. | From | Rule | | --------- | --------------------------------------------------------------------------------------------------------------------------------- | | A server | Open. Every `GET` works without setup. | | A browser | The page's origin must be a domain registered to the shop, and the plan decides: **Tumbuh** = `GET` only, **Skala** = everything. | Registering a domain is done by Sepetak for now: send the domain name from the shop owner's account. Always send `credentials: 'omit'`. Cookies never travel cross-origin, and a request that asks for them is refused by the browser. ## What a refusal looks like A cross-origin request the plan does not cover answers `403` with code `plan_limit`; an origin that is not one of the shop's domains answers `403 forbidden`. Both carry CORS headers, so your page can read them. See [Errors](/guide/errors). --- # Checkout in five calls Prices are computed server-side; the cart sends only `product_id` and `qty`. The buyer confirms their contact with a one-time code on every order. | Call | Returns | | ---------------------------------------------------------------- | ----------------------------------- | | `POST /shipping/rates` — quotes for cart + destination | `shipping_quote_id` | | `POST /checkout/draft` — cart becomes a priced draft | `draft_id`, `total`, `otp_required` | | `POST /checkout/otp/send` — code to the buyer's email / WhatsApp | `expires_at` | | `POST /checkout/otp/verify` — code right, order placed | `order`, `payment`, `track_token` | | `GET /orders/track/{token}` — status and payment instructions | `order.timeline`, `payment` | Field-level shapes for each call are in the [API reference](/api). ## Rules the server enforces - `track_token` is returned **once**. Keep it. - Render `order.timeline` and `order.status_label` as given; status can move from the merchant's side at any time. - Branch on `payment.kind`, never `payment.method`: which gateway fulfils a method is the server's decision. - Never send a code automatically, and never guess why one was refused. --- # Errors Every error is `{ "error": { "code", "message", "details" } }`. `message` is Indonesian and safe to show to the buyer. ```json { "error": { "code": "plan_limit", "message": "Akses API belum termasuk paket toko ini.", "details": {"quota": "api_access", "plan": "tumbuh", "need": "write"} } } ``` | Status · code | Meaning | | ----------------------------- | ---------------------------------------------------------------------------------- | | `403 plan_limit` | Plan does not cover this cross-origin request; `details.need` is `read` or `write` | | `403 forbidden` | Origin is not one of the shop's domains | | `400 unsupported_api_version` | Unknown date; `details.supported` lists the served ones | | `400` on a code | Wrong, expired or too many tries — one indistinguishable answer | | `409` | The order already moved; reload from `/orders/track/{token}` | --- # Limits and buyer links - Codes: five per contact per hour. Some routes are limited per IP — call checkout from the buyer's browser, not through a proxy, so each buyer counts as one. - Retry a `POST` with `Idempotency-Key`; a replayed answer carries `Idempotent-Replay`. - Confirmation emails and gateway returns point at the shop's **primary domain**. To land buyers on your site, make your domain primary and serve `/track/{token}` there. - A plan change reaches the API within a minute. --- # Quickstart ```js const BASE = 'https://.sepetak.com/api/v1'; const res = await fetch(`${BASE}/products?limit=12`, { headers: {'X-API-Version': '2026-09-09'}, credentials: 'omit', }); const {products} = await res.json(); ``` Pin `X-API-Version` to the date you wrote against. That shape never changes. Without the header you get the baseline (`2026-09-05`). See [Versions](/guide/versions). For a JavaScript site, the client our templates use ships as [`@sepetakhq/storefront-kit`](https://www.npmjs.com/package/@sepetakhq/storefront-kit): `configureApi({ baseUrl })`, then one function per endpoint with one error type. --- # Versions Each API version is a date. Old versions stay served; `X-API-Version` picks one. An unpinned request gets the baseline, which never changes. - What each version changed: [Changelog](/changelog). - The reference for the current version is at [/api](/api); older versions have their own pages, linked from the changelog. - The spec for any version: `https://sepetak.com/openapi.json?version=YYYY-MM-DD`; `info.x-versions` lists them all. A response shape only ever changes by cutting a new date. A field is never renamed or removed under a date that has shipped. --- # Sepetak storefront API 2026-09-09 The storefront API. The shop is the request's Host. Every field may be null; the schema shows each field's type when present. Errors are {error:{code,message,details}}. Send X-API-Version to pin the shape you were written against. Base URL: `https://.sepetak.com/api/v1`. Every error is the `Error` schema below. ## GET /categories List categories Response 200: - `categories[].image`: string - `categories[].image_srcset`: string - `categories[].key`: string - `categories[].label`: string - `categories[].material_spec`: string - `categories[].spec_label`: string - `categories[].subtitle`: string ## POST /checkout/draft Create draft Request: - `address`: string - `city`: string - `contact`: string - `courier_code`: string - `customer_name`: string - `items[].product_id`: string - `items[].qty`: integer - `items[].variant_id`: string - `payment_method`: string - `phone`: string - `shipping_quote_id`: string - `voucher_code`: string Response 200: - `contact.channel`: string - `contact.masked`: string - `discount_total`: integer - `draft_id`: string - `expires_at`: string (date-time) - `free_shipping`: boolean - `lines[].image`: string - `lines[].image_srcset`: string - `lines[].line_total`: integer - `lines[].name`: string - `lines[].preorder`: boolean - `lines[].product_id`: string - `lines[].promotion_name`: string - `lines[].qty`: integer - `lines[].sku`: string - `lines[].undiscounted_unit_price`: integer - `lines[].unit_price`: integer - `lines[].unlimited_stock`: boolean - `lines[].variant_id`: string - `lines[].variant_label`: string - `otp_required`: boolean - `payment_fee`: integer - `preorder_lead_days`: integer - `promotion_name`: string - `shipping_cost`: integer - `shipping_discount`: integer - `shipping_promotion_name`: string - `subtotal`: integer - `total`: integer - `unique_code`: integer - `voucher.amount`: integer - `voucher.applied`: boolean - `voucher.code`: string - `voucher.message`: string - `voucher.outbid`: integer - `voucher.outbid_name`: string - `voucher.reason`: string ## POST /checkout/otp/send Send code Request: - `draft_id`: string Response 200: - `channel`: string - `expires_in_seconds`: integer - `masked`: string - `sent`: boolean ## POST /checkout/otp/verify Verify and place Request: - `code`: string - `draft_id`: string Response 200: - `order.awb`: string - `order.buyer_claimed_at`: string (date-time) - `order.city`: string - `order.contact_masked`: string - `order.courier_code`: string - `order.customer_name`: string - `order.discount_total`: integer - `order.expires_at`: string (date-time) - `order.id`: string - `order.lines[].image`: string - `order.lines[].image_srcset`: string - `order.lines[].line_total`: integer - `order.lines[].name`: string - `order.lines[].preorder`: boolean - `order.lines[].qty`: integer - `order.lines[].sku`: string - `order.lines[].undiscounted_unit_price`: integer - `order.lines[].unit_price`: integer - `order.lines[].unlimited_stock`: boolean - `order.lines[].variant_label`: string - `order.number`: string - `order.payment_fee`: integer - `order.payment_method`: string - `order.placed_at`: string (date-time) - `order.preorder_lead_days`: integer - `order.preorder_ship_estimate`: string (date-time) - `order.promotion_name`: string - `order.shipping_cost`: integer - `order.shipping_discount`: integer - `order.shipping_promotion_name`: string - `order.status`: string - `order.status_label`: string - `order.subtotal`: integer - `order.timeline[].done`: boolean - `order.timeline[].key`: string - `order.timeline[].label`: string - `order.total`: integer - `order.unique_code`: integer - `order.voucher_code`: string - `payment.amount`: integer - `payment.expires_at`: string (date-time) - `payment.kind`: string - `payment.manual_account_holder`: string - `payment.manual_account_number`: string - `payment.manual_bank`: string - `payment.manual_qr_image_url`: string - `payment.manual_whatsapp_phone`: string - `payment.method`: string - `payment.qr_image_url`: string - `payment.qr_payload`: string - `payment.redirect_url`: string - `payment.status`: string - `payment.va_bank`: string - `payment.va_number`: string - `track_token`: string - `track_url`: string ## POST /orders/lookup/otp/send Send lookup code Request: - `contact`: string Response 200: - `channel`: string - `expires_in_seconds`: integer - `masked`: string - `sent`: boolean ## POST /orders/lookup/otp/verify List buyer's orders Request: - `code`: string - `contact`: string Response 200: - `orders[].awb`: string - `orders[].buyer_claimed_at`: string (date-time) - `orders[].city`: string - `orders[].contact_masked`: string - `orders[].courier_code`: string - `orders[].customer_name`: string - `orders[].discount_total`: integer - `orders[].expires_at`: string (date-time) - `orders[].id`: string - `orders[].lines[].image`: string - `orders[].lines[].image_srcset`: string - `orders[].lines[].line_total`: integer - `orders[].lines[].name`: string - `orders[].lines[].preorder`: boolean - `orders[].lines[].qty`: integer - `orders[].lines[].sku`: string - `orders[].lines[].undiscounted_unit_price`: integer - `orders[].lines[].unit_price`: integer - `orders[].lines[].unlimited_stock`: boolean - `orders[].lines[].variant_label`: string - `orders[].number`: string - `orders[].payment_fee`: integer - `orders[].payment_method`: string - `orders[].placed_at`: string (date-time) - `orders[].preorder_lead_days`: integer - `orders[].preorder_ship_estimate`: string (date-time) - `orders[].promotion_name`: string - `orders[].shipping_cost`: integer - `orders[].shipping_discount`: integer - `orders[].shipping_promotion_name`: string - `orders[].status`: string - `orders[].status_label`: string - `orders[].subtotal`: integer - `orders[].timeline[].done`: boolean - `orders[].timeline[].key`: string - `orders[].timeline[].label`: string - `orders[].total`: integer - `orders[].unique_code`: integer - `orders[].voucher_code`: string ## GET /orders/track/{token} Track order Response 200: - `order.awb`: string - `order.buyer_claimed_at`: string (date-time) - `order.city`: string - `order.contact_masked`: string - `order.courier_code`: string - `order.customer_name`: string - `order.discount_total`: integer - `order.expires_at`: string (date-time) - `order.id`: string - `order.lines[].image`: string - `order.lines[].image_srcset`: string - `order.lines[].line_total`: integer - `order.lines[].name`: string - `order.lines[].preorder`: boolean - `order.lines[].qty`: integer - `order.lines[].sku`: string - `order.lines[].undiscounted_unit_price`: integer - `order.lines[].unit_price`: integer - `order.lines[].unlimited_stock`: boolean - `order.lines[].variant_label`: string - `order.number`: string - `order.payment_fee`: integer - `order.payment_method`: string - `order.placed_at`: string (date-time) - `order.preorder_lead_days`: integer - `order.preorder_ship_estimate`: string (date-time) - `order.promotion_name`: string - `order.shipping_cost`: integer - `order.shipping_discount`: integer - `order.shipping_promotion_name`: string - `order.status`: string - `order.status_label`: string - `order.subtotal`: integer - `order.timeline[].done`: boolean - `order.timeline[].key`: string - `order.timeline[].label`: string - `order.total`: integer - `order.unique_code`: integer - `order.voucher_code`: string - `payment.amount`: integer - `payment.expires_at`: string (date-time) - `payment.kind`: string - `payment.manual_account_holder`: string - `payment.manual_account_number`: string - `payment.manual_bank`: string - `payment.manual_qr_image_url`: string - `payment.manual_whatsapp_phone`: string - `payment.method`: string - `payment.qr_image_url`: string - `payment.qr_payload`: string - `payment.redirect_url`: string - `payment.status`: string - `payment.va_bank`: string - `payment.va_number`: string ## POST /orders/track/{token}/payment Re-open payment Response 200: - `payment.amount`: integer - `payment.expires_at`: string (date-time) - `payment.kind`: string - `payment.manual_account_holder`: string - `payment.manual_account_number`: string - `payment.manual_bank`: string - `payment.manual_qr_image_url`: string - `payment.manual_whatsapp_phone`: string - `payment.method`: string - `payment.qr_image_url`: string - `payment.qr_payload`: string - `payment.redirect_url`: string - `payment.status`: string - `payment.va_bank`: string - `payment.va_number`: string ## POST /orders/track/{token}/payment/claim Claim manual payment Response 200: - `order.awb`: string - `order.buyer_claimed_at`: string (date-time) - `order.city`: string - `order.contact_masked`: string - `order.courier_code`: string - `order.customer_name`: string - `order.discount_total`: integer - `order.expires_at`: string (date-time) - `order.id`: string - `order.lines[].image`: string - `order.lines[].image_srcset`: string - `order.lines[].line_total`: integer - `order.lines[].name`: string - `order.lines[].preorder`: boolean - `order.lines[].qty`: integer - `order.lines[].sku`: string - `order.lines[].undiscounted_unit_price`: integer - `order.lines[].unit_price`: integer - `order.lines[].unlimited_stock`: boolean - `order.lines[].variant_label`: string - `order.number`: string - `order.payment_fee`: integer - `order.payment_method`: string - `order.placed_at`: string (date-time) - `order.preorder_lead_days`: integer - `order.preorder_ship_estimate`: string (date-time) - `order.promotion_name`: string - `order.shipping_cost`: integer - `order.shipping_discount`: integer - `order.shipping_promotion_name`: string - `order.status`: string - `order.status_label`: string - `order.subtotal`: integer - `order.timeline[].done`: boolean - `order.timeline[].key`: string - `order.timeline[].label`: string - `order.total`: integer - `order.unique_code`: integer - `order.voucher_code`: string - `payment.amount`: integer - `payment.expires_at`: string (date-time) - `payment.kind`: string - `payment.manual_account_holder`: string - `payment.manual_account_number`: string - `payment.manual_bank`: string - `payment.manual_qr_image_url`: string - `payment.manual_whatsapp_phone`: string - `payment.method`: string - `payment.qr_image_url`: string - `payment.qr_payload`: string - `payment.redirect_url`: string - `payment.status`: string - `payment.va_bank`: string - `payment.va_number`: string ## GET /products List products Response 200: - `limit`: integer - `offset`: integer - `products[].availability`: string - `products[].badge`: string - `products[].category_key`: string - `products[].category_label`: string - `products[].compare_at_price`: integer - `products[].has_variants`: boolean - `products[].id`: string - `products[].image`: string - `products[].image_height`: integer - `products[].image_srcset`: string - `products[].image_width`: integer - `products[].name`: string - `products[].on_sale`: boolean - `products[].preorder`: boolean - `products[].preorder_lead_days`: integer - `products[].price`: integer - `products[].price_max`: integer - `products[].sell_from`: string (date-time) - `products[].sell_until`: string (date-time) - `products[].slug`: string - `products[].sold_out`: boolean - `products[].stock`: integer - `products[].unlimited_stock`: boolean ## GET /products/{slug} Product by slug Response 200: - `availability`: string - `badge`: string - `care_copy`: string - `category_key`: string - `category_label`: string - `compare_at_price`: integer - `description`: string - `has_variants`: boolean - `id`: string - `image`: string - `image_height`: integer - `image_srcset`: string - `image_width`: integer - `images[].alt`: string - `images[].height`: integer - `images[].id`: string - `images[].srcset`: string - `images[].url`: string - `images[].width`: integer - `material_spec`: string - `name`: string - `on_sale`: boolean - `options[].name`: string - `options[].values[]`: string - `preorder`: boolean - `preorder_lead_days`: integer - `price`: integer - `price_max`: integer - `related[].availability`: string - `related[].badge`: string - `related[].category_key`: string - `related[].category_label`: string - `related[].compare_at_price`: integer - `related[].has_variants`: boolean - `related[].id`: string - `related[].image`: string - `related[].image_height`: integer - `related[].image_srcset`: string - `related[].image_width`: integer - `related[].name`: string - `related[].on_sale`: boolean - `related[].preorder`: boolean - `related[].preorder_lead_days`: integer - `related[].price`: integer - `related[].price_max`: integer - `related[].sell_from`: string (date-time) - `related[].sell_until`: string (date-time) - `related[].slug`: string - `related[].sold_out`: boolean - `related[].stock`: integer - `related[].unlimited_stock`: boolean - `sell_from`: string (date-time) - `sell_until`: string (date-time) - `slug`: string - `sold_out`: boolean - `spec_label`: string - `stock`: integer - `unlimited_stock`: boolean - `variants[].badge`: string - `variants[].compare_at_price`: integer - `variants[].id`: string - `variants[].image_id`: string - `variants[].on_sale`: boolean - `variants[].options.x`: string - `variants[].price`: integer - `variants[].sku`: string - `variants[].sold_out`: boolean - `variants[].stock`: integer - `variants[].weight_grams`: integer ## POST /shipping/destinations Search destinations Request: - `limit`: integer - `q`: string Response 200: - `results[].city`: string - `results[].district`: string - `results[].label`: string - `results[].province`: string - `results[].ref`: string - `results[].subdistrict`: string - `results[].zip_code`: string ## POST /shipping/destinations/resolve Resolve destination Request: - `ref`: string Response 200: - `destination.city`: string - `destination.district`: string - `destination.label`: string - `destination.province`: string - `destination.ref`: string - `destination.subdistrict`: string - `destination.zip_code`: string ## POST /shipping/rates Shipping quotes Request: - `destination_label`: string - `destination_ref`: string - `items[].product_id`: string - `items[].qty`: integer - `items[].variant_id`: string Response 200: - `destination_label`: string - `free_shipping`: boolean - `options[].cost`: integer - `options[].courier_code`: string - `options[].courier_name`: string - `options[].courier_service`: string - `options[].etd`: string - `options[].name`: string - `options[].quote_id`: string - `weight_grams`: integer ## GET /storefront/config Storefront config Response 200: - `brand.about_body`: string - `brand.about_image.alt`: string - `brand.about_image.srcset`: string - `brand.about_image.url`: string - `brand.about_title`: string - `brand.address_line1`: string - `brand.address_line2`: string - `brand.copyright`: string - `brand.email`: string - `brand.hero_eyebrow`: string - `brand.hero_images[].alt`: string - `brand.hero_images[].srcset`: string - `brand.hero_images[].url`: string - `brand.hero_stats[].label`: string - `brand.hero_stats[].value`: string - `brand.logo_image.alt`: string - `brand.logo_image.srcset`: string - `brand.logo_image.url`: string - `brand.nav[].label`: string - `brand.nav[].route`: string - `brand.origin_city`: string - `brand.product_care_copy`: string - `brand.product_shipping_copy`: string - `brand.section_copy.cats_heading`: string - `brand.section_copy.grid_see_all`: string - `brand.section_copy.product_care_heading`: string - `brand.section_copy.product_details_heading`: string - `brand.section_copy.product_shipping_heading`: string - `brand.section_copy.story_cta`: string - `brand.section_copy.story_eyebrow`: string - `brand.social_image.alt`: string - `brand.social_image.srcset`: string - `brand.social_image.url`: string - `brand.tagline`: string - `brand.tagline_short`: string - `brand.template`: string - `brand.whatsapp_text`: string - `brand.whatsapp_url`: string - `brand.wordmark_accent`: string - `brand.wordmark_primary`: string - `commerce.couriers[].code`: string - `commerce.couriers[].cost`: integer - `commerce.couriers[].enabled`: boolean - `commerce.couriers[].eta`: string - `commerce.couriers[].name`: string - `commerce.couriers[].service`: string - `commerce.couriers[].services[]`: string - `commerce.free_shipping_disabled`: boolean - `commerce.free_shipping_min`: integer - `commerce.payment_methods[].code`: string - `commerce.payment_methods[].enabled`: boolean - `commerce.payment_methods[].fee_basis_pt`: integer - `commerce.payment_methods[].fee_flat`: integer - `commerce.payment_methods[].max_order_total`: integer - `commerce.payment_methods[].min_order_total`: integer - `commerce.payment_methods[].name`: string - `commerce.payment_methods[].sub`: string - `commerce.shipping.mode`: string - `contact.channel`: string - `payment.account_holder`: string - `payment.qris_merchant_name`: string - `payment.qris_nmid`: string - `tenant.name`: string - `tenant.slug`: string ## GET /storefront/home Home page Response 200: - `announcement.enabled`: boolean - `announcement.text`: string - `sections[].banner.bg`: string - `sections[].banner.cta`: string - `sections[].banner.text`: string - `sections[].campaign.badge`: string - `sections[].campaign.discount_type`: string - `sections[].campaign.discount_value`: integer - `sections[].campaign.ends_at`: string (date-time) - `sections[].campaign.products[].availability`: string - `sections[].campaign.products[].badge`: string - `sections[].campaign.products[].category_key`: string - `sections[].campaign.products[].category_label`: string - `sections[].campaign.products[].compare_at_price`: integer - `sections[].campaign.products[].has_variants`: boolean - `sections[].campaign.products[].id`: string - `sections[].campaign.products[].image`: string - `sections[].campaign.products[].image_height`: integer - `sections[].campaign.products[].image_srcset`: string - `sections[].campaign.products[].image_width`: integer - `sections[].campaign.products[].name`: string - `sections[].campaign.products[].on_sale`: boolean - `sections[].campaign.products[].preorder`: boolean - `sections[].campaign.products[].preorder_lead_days`: integer - `sections[].campaign.products[].price`: integer - `sections[].campaign.products[].price_max`: integer - `sections[].campaign.products[].sell_from`: string (date-time) - `sections[].campaign.products[].sell_until`: string (date-time) - `sections[].campaign.products[].slug`: string - `sections[].campaign.products[].sold_out`: boolean - `sections[].campaign.products[].stock`: integer - `sections[].campaign.products[].unlimited_stock`: boolean - `sections[].campaign.title`: string - `sections[].cats[].image`: string - `sections[].cats[].image_srcset`: string - `sections[].cats[].key`: string - `sections[].cats[].label`: string - `sections[].cats[].material_spec`: string - `sections[].cats[].spec_label`: string - `sections[].cats[].subtitle`: string - `sections[].grid.products[].availability`: string - `sections[].grid.products[].badge`: string - `sections[].grid.products[].category_key`: string - `sections[].grid.products[].category_label`: string - `sections[].grid.products[].compare_at_price`: integer - `sections[].grid.products[].has_variants`: boolean - `sections[].grid.products[].id`: string - `sections[].grid.products[].image`: string - `sections[].grid.products[].image_height`: integer - `sections[].grid.products[].image_srcset`: string - `sections[].grid.products[].image_width`: integer - `sections[].grid.products[].name`: string - `sections[].grid.products[].on_sale`: boolean - `sections[].grid.products[].preorder`: boolean - `sections[].grid.products[].preorder_lead_days`: integer - `sections[].grid.products[].price`: integer - `sections[].grid.products[].price_max`: integer - `sections[].grid.products[].sell_from`: string (date-time) - `sections[].grid.products[].sell_until`: string (date-time) - `sections[].grid.products[].slug`: string - `sections[].grid.products[].sold_out`: boolean - `sections[].grid.products[].stock`: integer - `sections[].grid.products[].unlimited_stock`: boolean - `sections[].grid.title`: string - `sections[].hero.cta`: string - `sections[].hero.em`: string - `sections[].hero.sub`: string - `sections[].hero.title`: string - `sections[].id`: string - `sections[].story.text`: string - `sections[].story.title`: string - `sections[].type`: string - `server_time`: string (date-time) ## GET /storefront/page/{key} Page by key Response 200: - `announcement.enabled`: boolean - `announcement.text`: string - `sections[].banner.bg`: string - `sections[].banner.cta`: string - `sections[].banner.text`: string - `sections[].campaign.badge`: string - `sections[].campaign.discount_type`: string - `sections[].campaign.discount_value`: integer - `sections[].campaign.ends_at`: string (date-time) - `sections[].campaign.products[].availability`: string - `sections[].campaign.products[].badge`: string - `sections[].campaign.products[].category_key`: string - `sections[].campaign.products[].category_label`: string - `sections[].campaign.products[].compare_at_price`: integer - `sections[].campaign.products[].has_variants`: boolean - `sections[].campaign.products[].id`: string - `sections[].campaign.products[].image`: string - `sections[].campaign.products[].image_height`: integer - `sections[].campaign.products[].image_srcset`: string - `sections[].campaign.products[].image_width`: integer - `sections[].campaign.products[].name`: string - `sections[].campaign.products[].on_sale`: boolean - `sections[].campaign.products[].preorder`: boolean - `sections[].campaign.products[].preorder_lead_days`: integer - `sections[].campaign.products[].price`: integer - `sections[].campaign.products[].price_max`: integer - `sections[].campaign.products[].sell_from`: string (date-time) - `sections[].campaign.products[].sell_until`: string (date-time) - `sections[].campaign.products[].slug`: string - `sections[].campaign.products[].sold_out`: boolean - `sections[].campaign.products[].stock`: integer - `sections[].campaign.products[].unlimited_stock`: boolean - `sections[].campaign.title`: string - `sections[].cats[].image`: string - `sections[].cats[].image_srcset`: string - `sections[].cats[].key`: string - `sections[].cats[].label`: string - `sections[].cats[].material_spec`: string - `sections[].cats[].spec_label`: string - `sections[].cats[].subtitle`: string - `sections[].grid.products[].availability`: string - `sections[].grid.products[].badge`: string - `sections[].grid.products[].category_key`: string - `sections[].grid.products[].category_label`: string - `sections[].grid.products[].compare_at_price`: integer - `sections[].grid.products[].has_variants`: boolean - `sections[].grid.products[].id`: string - `sections[].grid.products[].image`: string - `sections[].grid.products[].image_height`: integer - `sections[].grid.products[].image_srcset`: string - `sections[].grid.products[].image_width`: integer - `sections[].grid.products[].name`: string - `sections[].grid.products[].on_sale`: boolean - `sections[].grid.products[].preorder`: boolean - `sections[].grid.products[].preorder_lead_days`: integer - `sections[].grid.products[].price`: integer - `sections[].grid.products[].price_max`: integer - `sections[].grid.products[].sell_from`: string (date-time) - `sections[].grid.products[].sell_until`: string (date-time) - `sections[].grid.products[].slug`: string - `sections[].grid.products[].sold_out`: boolean - `sections[].grid.products[].stock`: integer - `sections[].grid.products[].unlimited_stock`: boolean - `sections[].grid.title`: string - `sections[].hero.cta`: string - `sections[].hero.em`: string - `sections[].hero.sub`: string - `sections[].hero.title`: string - `sections[].id`: string - `sections[].story.text`: string - `sections[].story.title`: string - `sections[].type`: string - `server_time`: string (date-time) ## Schema: Error - `error.code`: string - `error.details.x`: string - `error.message`: string ## Schema: ErrorDestinationUnserved - `error.code`: string - `error.details.alternatives[].city`: string - `error.details.alternatives[].district`: string - `error.details.alternatives[].label`: string - `error.details.alternatives[].province`: string - `error.details.alternatives[].ref`: string - `error.details.alternatives[].subdistrict`: string - `error.details.alternatives[].zip_code`: string - `error.message`: string