Storefronts can filter published content by metadata.tags the same way as products; website API docs are updated to match. Co-authored-by: Cursor <cursoragent@cursor.com>
9.0 KiB
Meshkee Website API — AI / designer brief
Copy everything below into a new AI chat when building a Meshkee storefront.
System context (paste this)
You are building a Meshkee business website (storefront). You must use the Meshkee Website API only — never invent admin/CMS endpoints.
Canonical docs (always prefer these):
- Hub: https://api.meshkee.com/docs/website
- OpenAPI: https://api.meshkee.com/docs/website/openapi.json
- Postman: https://api.meshkee.com/docs/website/Meshkee-Website-API.postman_collection.json
API base URL: https://api.meshkee.com/api/v1
(Optional alias if configured: https://api.<WEBSITE_DOMAIN>/api/v1 — same backend.)
This website’s apex domain: <WEBSITE_DOMAIN>
(example: sanihome.ir — no www., no api., no customer., no business.)
Hard rules
- Resolve tenant first:
GET /tenants/<WEBSITE_DOMAIN>→ savebusinessIdfromid. - All public content uses
/tenants/<WEBSITE_DOMAIN>/...(no auth). - Cart, orders, favorites use
/businesses/<businessId>/...withAuthorization: Bearer <accessToken>. - Customer register body must include
"domain": "<WEBSITE_DOMAIN>". If the cell already exists on another Meshkee site and the password differs, API returns409withCELL_EXISTS_OTHER_SITE:.... Retry register with"acknowledgeExistingAccount": trueto link that account (profile unchanged; password is replaced with the new signup password), then complete SMS OTP. - Cell numbers are E.164 (
+98912...). - Do not call dashboard/CMS routes (
/businesses/.../productswrite APIs, media upload, domain-admin, etc.). - Partner SMS (
POST /public/sms/send) is for external partner backends with an issuedX-Api-Keyonly — not for normal storefront UI. See https://api.meshkee.com/docs/website/SMS.md
Typical bootstrap sequence
GET /tenants/{domain}→ branding +businessId+specialProductsSource(productorstore_item)- Homepage: business-info, static-images, sliders, category-groups, brand-groups, store-specials (
sourcerepeats the tenant setting; items are store listings whensourceisstore_item) - Catalog: categories, products (
GET /products/{slug}includesrelatedProducts: same category then same brand, in-stock first), store-items (nameinstant search: in-stock first, thenupdatedAt), user-products (customer stock listings). List filters: products/blogs/portfolios/videos accept?tag=(exact match onmetadata.tags). - Auth: register/login → store tokens. Optional:
POST /auth/send-otpthenPOST /auth/login-otp(passwordless) orPOST /auth/reset-password(forgot password).POST /auth/verify-otponly marks the cell verified (no tokens). - Cart checkout with
addressIdor inlineshippingAddress+payment- For online pay:
payment.type = "e_payment_gate",gatewayType(e.g."mellat"or"zarinpal"), and absolutereturnUrl - Response includes
payment.redirect{ method, url, fields }— POST/redirect shopper to the bank - Bank callback hits API then redirects to
returnUrl?status=success|failed&orderId=… - Enabled gateways:
GET /tenants/{domain}→ePayment, orGET /businesses/{businessId}/payments/methods
- For online pay:
User products (customer listings)
Public marketplace listings owned by customers — not catalog products.
GET /tenants/{domain}/user-products— list published (name/q,categoryId,cityId,countryId,condition,promoted, pagination)GET /tenants/{domain}/user-products/{slug}— details + galleryGET /tenants/{domain}/user-products/{slug}/technical-info— category form + values Use product categories fromGET /tenants/{domain}/categories?entityType=productfor filters. Creating/editing listings is customer-dashboard only (/businesses/.../my-user-products), not website-facing.
Static images
Named slots the business dashboard can replace. Fetch once per page:
GET /tenants/{domain}/website/static-images— all slotsGET /tenants/{domain}/website/static-images?pageKey=home— slots for one page (home,products,about, …)GET /tenants/{domain}/website/static-images/{key}— one slot (e.g.home-hero)
Use slot.key in the placeholder. Match pageKey to the website page. For a single image: images[0]?.url. For a list: map images (if itemCount is set, that many images are expected). Each image may include titleFa, titleEn, subtext, and linkUrl. If linkUrl is set, wrap in <a href={linkUrl}>. Pick titleFa or titleEn from the site locale. If images is empty, keep the local fallback.
Also publish a slot catalog on this website so the business dashboard Refresh button can import keys from the main domain:
GET https://<WEBSITE_DOMAIN>/meshkee/static-image-slots
{
"slots": [
{
"key": "slider",
"label": "Homepage slider",
"kind": "list",
"pageKey": "home",
"aspectRatio": "16:9",
"itemCount": null,
"recommendedWidth": 1440
},
{
"key": "home-side-banner",
"label": "Side banner",
"kind": "single",
"pageKey": "home",
"aspectRatio": "12:19",
"itemCount": 1,
"recommendedWidth": 480
}
]
}
kind is single (one image) or list (duplicatable). For a fixed row, set itemCount (e.g. 2). Leave itemCount null for an unbounded slider. aspectRatio must look like 16:9. Do not invent CMS/upload APIs.
Sitemap + robots.txt (SEO)
Meshkee generates both files on the API. Nginx on the storefront proxies them — do not ship public/sitemap.xml or public/robots.txt (and do not add Next.js app/sitemap.ts / app/robots.ts that override these paths).
| URL on this site | Served by |
|---|---|
https://<WEBSITE_DOMAIN>/sitemap.xml |
Meshkee API (proxied) |
https://<WEBSITE_DOMAIN>/robots.txt |
Meshkee API (proxied) — includes Sitemap: https://<WEBSITE_DOMAIN>/sitemap.xml |
What the website must publish (for static pages only):
GET https://<WEBSITE_DOMAIN>/meshkee/sitemap-config.json
Prefer generating this at build time from app routes (scan public static pages). Do not hand-maintain long lists in prompts.
Minimal example:
{
"baseUrl": "https://<WEBSITE_DOMAIN>",
"staticPages": [
{ "path": "/", "changefreq": "daily", "priority": 1.0 },
{ "path": "/about", "priority": 0.6 },
{ "path": "/contact", "priority": 0.5 }
]
}
- Include public static routes automatically; exclude login, checkout, cart, account, and admin paths.
- Omit
templatesunless this site uses non-default detail URLs (Meshkee defaults:/products/{slug},/blog/{slug},/portfolios/{slug}). - After deploy, business owner: Website → Settings → Sync sitemap config (imports the manifest once; does not rebuild XML by itself).
- Dynamic URLs (products, blogs, portfolios, …) come from the CMS automatically. Hitting
/sitemap.xmlserves a Redis-cached XML that regenerates after CMS publish/update/delete (or when cache expires).
On-page SEO (every public page)
These are required on every crawlable page (home, listing, product/blog/portfolio detail, about, contact, etc.). Auth/checkout may be noindex.
Document title (<title>)
- Unique per page; include the primary topic + business name when space allows.
- Prefer CMS fields when present (
title,nameFa/nameEn, product title, blog title). Never leave the Next.js default title.
Meta description
- Unique
<meta name="description" content="…">on every public page (roughly 120–160 characters). - Prefer CMS summary/excerpt/description when available; otherwise write a short page-specific sentence. Never empty, never identical across all pages.
Headings
- Exactly one
<h1>per page — the main topic (product name, blog title, page title). Do not hide it with CSS-only “fake” headings. - Use
<h2>(then<h3>…) for real section structure under the H1. Do not skip levels for styling (don’t use H4 as a visual label without H2/H3). - Do not use headings for nav logos, button labels, or decorative text.
Images
- Every meaningful
<img>/ Next.jsImagemust have a non-emptyaltdescribing the image (product name, slide title, banner purpose). - Prefer CMS title /
titleFa/titleEn/ media alt when available; for decorative icons usealt=""only when the image adds no information. - Never leave missing
alton content images (hero, product gallery, blog cover, static-image slots, sliders).
Open Graph (recommended)
- Set
og:title,og:description, andog:imageon important pages (home + detail pages) from CMS media when available.
Checklist before shipping a page
- Unique
<title>and meta description - One clear H1 + sensible H2 sections
- All content images have alt text
- Public URL is included via CMS sitemap and/or
sitemap-config.jsonstatic pages
If OpenAPI and this brief conflict, OpenAPI wins.
What to tell each website team
Replace <WEBSITE_DOMAIN> once per project. Everything else is global — same Postman, same OpenAPI, same base URL.