Menus and content
Build the navigation and content pages of your headless store.
You will build the shell of a store in Lima: the header menu, the "Polos" category, and the "Envíos" shipping page. All with the Storefront API, no token.
Requirements
- The store slug or one of its domains, for
x-Store-Domain. See Headless store. - Categories and pages published in the store.
Read the settings
GET / returns the store, its branding, its SEO, its published pages
(cms.pages), and its active menus (cms.menus) in one call. Fetch it once per
request and reuse it. GET / returns only cms.settings.
curl https://storefront.ecomiq.pe/v1/settings \
-H "x-Store-Domain: mi-tienda"{
"store": { "name": "Mi tienda", "currency": "PEN", "visibility": "public" },
"cms": {
"settings": {
"branding": { "logoUrl": "https://…/logo.webp" },
"seo": { "siteName": "Mi tienda", "robotsIndex": true },
"contact": { "whatsapp": "+51987654321" }
}
}
}The next steps reuse API and headers.
- With
visibility: "private", the store asks for a password. See Unlock a private store. - With
robotsIndex: false, block the whole site inrobots.txt.
Render the menu
Menus arrive in cms.menus and in GET /. To fetch just one, use
GET /. A new store comes with main, footer, and account.
curl https://storefront.ecomiq.pe/v1/menus/main \
-H "x-Store-Domain: mi-tienda"{
"key": "main",
"name": "Menú principal",
"items": [
{
"label": "Polos",
"url": "/categories/polos",
"reference": { "type": "category", "id": "7204558912004327982" },
"order": 1,
"visible": true,
"children": []
}
]
}- Items arrive as saved, unfiltered and unsorted. Skip those with
visible: falseand sort byorder, inchildrentoo. - Links to products and categories arrive with their current URL, even if the
merchant changes it.
referencetells you which one they point to.
Show a category
GET / returns the category with its SEO, its breadcrumb,
and its subcategories. Then fetch its products with categoryId, as in
Headless store.
curl https://storefront.ecomiq.pe/v1/categories/polos \
-H "x-Store-Domain: mi-tienda"
curl "https://storefront.ecomiq.pe/v1/products?categoryId=7204558912004327982" \
-H "x-Store-Domain: mi-tienda"{
"id": "7204558912004327982",
"name": "Polos",
"url": "/categories/polos",
"metaTitle": "Polos de algodón pima",
"productCount": 24,
"ancestors": [
{
"id": "7204558912004329210",
"name": "Hombre",
"url": "/categories/hombre"
}
],
"children": []
}ancestorsgoes from the root to the parent.categoryIdskips subcategories.GET /returns all of them in a flat list sorted byv1/ categories order. Build the tree withparentId.GET /lists the brands.v1/ brands GET /lists collections. For one, usev1/ collections GET /andv1/ collections/ {slug} collectionId. Withoutsort, products follow the order the merchant chose.
Show pages and articles
GET / returns the published version of a page. All of them
also arrive in cms.pages.
curl https://storefront.ecomiq.pe/v1/pages/envios \
-H "x-Store-Domain: mi-tienda"{
"name": "Envíos",
"slug": "envios",
"layout": "content",
"settings": {
"title": "Envíos",
"html": "<p>Entregamos en Lima en 24 a 48 horas.</p>",
"seo": { "title": "Envíos", "description": "Tiempos y costos de envío." }
},
"sections": []
}- With
layout: "content", the text comes insettings:title,html, andseo. The home page has the slughomeand bringssectionswithblocks. - The merchant writes the
html. Sanitize it before you insert it.
For the blog, GET / lists published articles, newest first.
GET / returns one with its body in HTML, which you also
sanitize.
curl "https://storefront.ecomiq.pe/v1/articles?tag=pima&limit=12" \
-H "x-Store-Domain: mi-tienda"Filter with folder or tag (exact value) or search (title and excerpt).
limit goes from 1 to 100 and defaults to 24; there are no further pages.
Add the search box
GET / suggests products while the shopper types. Send
search, 2 to 80 characters, and limit, 1 to 20.
curl "https://storefront.ecomiq.pe/v1/search/autocomplete?search=polo&limit=6" \
-H "x-Store-Domain: mi-tienda"[
{
"name": "Polo pima azul",
"url": "/products/polo-pima-azul",
"imageUrl": "https://…/polo.webp"
}
]For the results page, use GET /.
Resolve a URL
Before you render a route, check whether the merchant redirected it.
curl "https://storefront.ecomiq.pe/v1/redirects/resolve?path=/ofertas" \
-H "x-Store-Domain: mi-tienda"{
"redirect": {
"location": "/categories/polos",
"statusCode": 301,
"preserveQuery": true
}
}Respond with statusCode and location. With preserveQuery: true, append
the request query. Without a redirect, redirect is null. The path must
match exactly, including case; only a trailing / is ignored.
Then, in your catch-all route, ask what lives at that path. Send only the path, without domain or query.
curl "https://storefront.ecomiq.pe/v1/urls/resolve?path=/categories/polos" \
-H "x-Store-Domain: mi-tienda"{
"kind": "category",
"url": "/categories/polos",
"handle": "polos",
"product": null,
"category": { "id": "7204558912004327982", "name": "Polos" }
}productorcategory: render the product or the category you get.redirect: respond301tourl. It happens with old URLs, uppercase paths, or a trailing/.- Only products and categories resolve. Otherwise:
404Url.NotFound.
If you store links to products or categories in your own content,
POST / returns them with their current URL and reference, in
order. It takes up to 100, keeps the query, and leaves external links as is.
curl -X POST https://storefront.ecomiq.pe/v1/urls/links \
-H "x-Store-Domain: mi-tienda" \
-H "Content-Type: application/json" \
-d '{ "links": [{ "url": "/products/polo-azul?talla=m" }] }'Publish the sitemap
GET / lists the canonical URLs of visible products, categories, and
collections. Each page holds up to 1000 products; the first one adds the
categories and collections.
curl "https://storefront.ecomiq.pe/v1/sitemap?page=1" \
-H "x-Store-Domain: mi-tienda"{
"items": [{ "url": "/categories/polos", "updatedOn": null }],
"nextPage": 2,
"totalPages": 3
}URLs are paths: prefix your store's domain. The home page, pages, and blog are
not included; add them from cms.pages and GET /. If
totalPages is greater than 1, publish an index with one sitemap per page.
Show physical stores
GET / lists active physical stores, by name. Filter with
search by name or city. It returns 20 by default; use limit and cursor
to get more. In a marketplace it returns the main store's; pass sellerId for
a seller's. GET / returns one.
curl "https://storefront.ecomiq.pe/v1/store-locations?search=Miraflores" \
-H "x-Store-Domain: mi-tienda"{
"data": [
{
"name": "Tienda Miraflores",
"address": "Av. Larco 1234",
"city": "Lima",
"schedules": [
{
"dayOfWeek": "monday",
"openTime": "10:00:00",
"closeTime": "21:00:00"
}
]
}
],
"hasNextPage": false,
"nextCursor": null
}Unlock a private store
A store with visibility: "private" answers 401 with Common.Unauthorized
on every call except / and /. Ask the visitor
for the password and send it from your server.
curl -X POST https://storefront.ecomiq.pe/v1/access/unlock \
-H "x-Store-Domain: mi-tienda" \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{ "accessKey": "<password>" }'It returns { "unlocked": true } and sets the ecomiq_store_access cookie,
valid for 12 hours. Send it back on every following call (-b cookies.txt in
cURL). If the merchant changes the password, the cookie stops working.
Errors
code | Status | What to do |
|---|---|---|
Url.NotFound | 404 | Nothing is published at that path. Show your 404 page. |
cms.page.not_found | 404 | The page does not exist or is not published. |
cms.article.not_found | 404 | The article does not exist or is not published. |
Category.NotFound | 404 | The category does not exist or is inactive. |
cms.menu.not_found | 404 | The menu does not exist or is inactive. Skip it. |
Common.NotFound | 404 | The collection, location, or store does not exist. |
Common.Validation | 400 | Check errors. For example, a one-character search. |
Common.Unauthorized | 401 | The store is private. Ask for the password. |
Store.AccessDenied | 401 | The password does not match. |
Common.ServiceUnavailable | 503 | Search is not responding. Retry in a few seconds. |
Next steps
- Headless store: cart and checkout.
- Content & Storefront: reference.
- Errors: format and retries.
Last updated: