Files
backend/docs/website-api/Meshkee-Website-API.postman_collection.json
T
Alireza HassaniandCursor 9a020d748d Align storefront content URLs with live slug-based routes.
Use /blog/{slug}, /portfolio/{slug}, and /instruction/{slug} in sitemap defaults and emit CMS slugs for blogs/portfolios instead of title-derived paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-08 14:51:12 +03:30

2209 lines
67 KiB
JSON

{
"info": {
"name": "Meshkee Website API (Global)",
"description": "# Meshkee Website API — Global reference for all storefronts\n\nCanonical docs: https://api.meshkee.com/docs/website\n\nThis collection is **not** tied to one business. Every Meshkee website (any domain) uses the same endpoints.\n\n## How multi-tenancy works\n1. Set collection variable `domain` = the **website apex** only (e.g. `example.com`, `sanihome.ir`). Never use `www.` / `api.` / `customer.` / `business.` here.\n2. Set `baseUrl` (see below).\n3. Run **Resolve tenant** → saves `businessId`.\n4. Public content: `/tenants/{{domain}}/...` (no auth).\n5. **Do not** build login or checkout on the storefront. Auth, server cart, addresses, and payment live on `https://customer.{{domain}}`. The shop mini-cart is a local guest cart (`meshkee-guest-cart`); add-to-cart loads `GET /tenants/{{domain}}/store-items/by-product/{productId}`, lists variants if there is more than one, then adds the chosen `storeItemVariantId`. Continue redirects to the customer dashboard (see AI_PROMPT.md).\n6. Favorites (optional) may use `/businesses/{{businessId}}/...` with Bearer if the shopper already has dashboard cookies.\n\n## baseUrl options (same Nest API)\n- Preferred central: `https://api.meshkee.com/api/v1`\n- Per-site alias (if DNS+SSL configured): `https://api.{{domain}}/api/v1`\n- Local: `http://localhost:3000/api/v1`\n\nTenant is always taken from the **path** (`/tenants/{domain}`), not from the API hostname.\n\n## Auth\n- Public: no header\n- Customer dashboard: `Authorization: Bearer {{accessToken}}`\n- Storefronts must **not** call register/login — redirect to `https://customer.{{domain}}/login`\n- The dashboard writes parent-domain cookies `meshkee_customer_access_token` and `meshkee_customer_refresh_token` on `Domain=.{{domain}}` (`Path=/`, `SameSite=Lax`). The shop only **reads** them. Do **not** use `/auth/handoff` for shoppers (staff SSO into the business dashboard).\n\n## Not in this collection\nCMS / dashboard / super-admin APIs (staff only).\n",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"variable": [
{
"key": "baseUrl",
"value": "https://api.meshkee.com/api/v1"
},
{
"key": "domain",
"value": "YOUR_WEBSITE_DOMAIN"
},
{
"key": "businessId",
"value": ""
},
{
"key": "accessToken",
"value": ""
},
{
"key": "refreshToken",
"value": ""
},
{
"key": "productId",
"value": ""
},
{
"key": "productSlug",
"value": ""
},
{
"key": "userProductId",
"value": ""
},
{
"key": "userProductSlug",
"value": ""
},
{
"key": "blogId",
"value": ""
},
{
"key": "blogSlug",
"value": ""
},
{
"key": "blogCategoryId",
"value": ""
},
{
"key": "portfolioId",
"value": ""
},
{
"key": "portfolioSlug",
"value": ""
},
{
"key": "portfolioCategoryId",
"value": ""
},
{
"key": "storeItemVariantId",
"value": ""
},
{
"key": "cartItemId",
"value": ""
},
{
"key": "orderId",
"value": ""
},
{
"key": "commentId",
"value": ""
},
{
"key": "expertReviewId",
"value": ""
},
{
"key": "addressId",
"value": ""
},
{
"key": "countryId",
"value": ""
},
{
"key": "provinceId",
"value": ""
},
{
"key": "cityId",
"value": ""
},
{
"key": "categoryId",
"value": ""
},
{
"key": "brandId",
"value": ""
},
{
"key": "smsApiKey",
"value": ""
}
],
"item": [
{
"name": "Tenant",
"item": [
{
"name": "Resolve tenant by domain",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.id) pm.collectionVariables.set(\"businessId\", String(json.id));",
"}"
]
}
}
],
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}"
}
}
]
},
{
"name": "Auth",
"item": [
{
"name": "Register (customer on website)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.accessToken) pm.collectionVariables.set('accessToken', json.accessToken);",
" if (json.refreshToken) pm.collectionVariables.set('refreshToken', json.refreshToken);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"cellNumber\": \"+98XXXXXXXXXX\",\n \"password\": \"min8chars\",\n \"firstName\": \"First\",\n \"lastName\": \"Last\",\n \"email\": \"optional@example.com\",\n \"domain\": \"{{domain}}\",\n \"acknowledgeExistingAccount\": false\n}"
},
"url": "{{baseUrl}}/auth/register"
}
},
{
"name": "Login - Customer",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" pm.collectionVariables.set('accessToken', json.accessToken);",
" pm.collectionVariables.set('refreshToken', json.refreshToken);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"cellNumber\": \"+98XXXXXXXXXX\",\n \"password\": \"YOUR_PASSWORD\"\n}"
},
"url": "{{baseUrl}}/auth/login"
}
},
{
"name": "Login with OTP",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" pm.collectionVariables.set('accessToken', json.accessToken);",
" pm.collectionVariables.set('refreshToken', json.refreshToken);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"cellNumber\": \"+98XXXXXXXXXX\",\n \"code\": \"123456\"\n}"
},
"url": "{{baseUrl}}/auth/login-otp"
}
},
{
"name": "Reset password (SMS OTP)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"cellNumber\": \"+98XXXXXXXXXX\",\n \"code\": \"123456\",\n \"newPassword\": \"newpassword123\"\n}"
},
"url": "{{baseUrl}}/auth/reset-password"
}
},
{
"name": "Me (current user)",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": "{{baseUrl}}/auth/me"
}
},
{
"name": "Refresh token",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" pm.collectionVariables.set('accessToken', json.accessToken);",
" pm.collectionVariables.set('refreshToken', json.refreshToken);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"refreshToken\": \"{{refreshToken}}\"\n}"
},
"url": "{{baseUrl}}/auth/refresh"
}
},
{
"name": "Send OTP (SMS disabled)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"cellNumber\": \"+989124444444\"\n}"
},
"url": "{{baseUrl}}/auth/send-otp"
}
},
{
"name": "Verify OTP (SMS disabled)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"cellNumber\": \"+989124444444\",\n \"code\": \"123456\"\n}"
},
"url": "{{baseUrl}}/auth/verify-otp"
}
},
{
"name": "Update profile",
"request": {
"method": "PATCH",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"firstName\": \"Ali\",\n \"lastName\": \"Customer\",\n \"email\": \"ali@example.com\"\n}"
},
"url": "{{baseUrl}}/auth/profile"
}
},
{
"name": "Change password",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"currentPassword\": \"password\",\n \"newPassword\": \"password123\"\n}"
},
"url": "{{baseUrl}}/auth/change-password"
}
}
]
},
{
"name": "Addresses",
"item": [
{
"name": "List my addresses",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set(\"addressId\", json.items[0].id);",
"}"
]
}
}
],
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": "{{baseUrl}}/auth/addresses"
}
},
{
"name": "Create address",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.address?.id) pm.collectionVariables.set(\"addressId\", json.address.id);",
"}"
]
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"province\": \"Tehran\",\n \"city\": \"Tehran\",\n \"address\": \"123 Example Street\",\n \"postalCode\": \"1234567890\",\n \"landline\": \"02112345678\"\n}"
},
"url": "{{baseUrl}}/auth/addresses"
}
},
{
"name": "Update address",
"request": {
"method": "PATCH",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"province\": \"Tehran\",\n \"city\": \"Tehran\",\n \"address\": \"456 Updated Street\",\n \"postalCode\": \"1234567890\"\n}"
},
"url": "{{baseUrl}}/auth/addresses/{{addressId}}"
}
},
{
"name": "Delete address",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": "{{baseUrl}}/auth/addresses/{{addressId}}"
}
}
]
},
{
"name": "Comments",
"item": [
{
"name": "Submit comment (website)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.comment?.id) pm.collectionVariables.set('commentId', json.comment.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"entityType\": \"product\",\n \"entityId\": \"{{productId}}\",\n \"authorName\": \"Website Visitor\",\n \"authorEmail\": \"visitor@example.com\",\n \"text\": \"Great product! Would recommend.\"\n}"
},
"url": "{{baseUrl}}/tenants/{{domain}}/comments"
}
},
{
"name": "List approved comments (website)",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/comments?entityType=product&entityId={{productId}}",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"comments"
],
"query": [
{
"key": "entityType",
"value": "product"
},
{
"key": "entityId",
"value": "{{productId}}"
}
]
}
}
},
{
"name": "Submit comment on blog (generic API)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.comment?.id) pm.collectionVariables.set('commentId', json.comment.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"entityType\": \"blog\",\n \"entityId\": \"{{blogId}}\",\n \"authorName\": \"Blog Reader\",\n \"authorEmail\": \"reader@example.com\",\n \"text\": \"Great article, thanks for sharing!\"\n}"
},
"url": "{{baseUrl}}/tenants/{{domain}}/comments"
}
},
{
"name": "List approved comments for blog (generic API)",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/comments?entityType=blog&entityId={{blogId}}",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"comments"
],
"query": [
{
"key": "entityType",
"value": "blog"
},
{
"key": "entityId",
"value": "{{blogId}}"
}
]
}
}
},
{
"name": "Submit comment on portfolio (generic API)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.comment?.id) pm.collectionVariables.set('commentId', json.comment.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"entityType\": \"portfolio\",\n \"entityId\": \"{{portfolioId}}\",\n \"authorName\": \"Portfolio Visitor\",\n \"authorEmail\": \"visitor@example.com\",\n \"text\": \"Beautiful work!\"\n}"
},
"url": "{{baseUrl}}/tenants/{{domain}}/comments"
}
},
{
"name": "List approved comments for portfolio (generic API)",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/comments?entityType=portfolio&entityId={{portfolioId}}",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"comments"
],
"query": [
{
"key": "entityType",
"value": "portfolio"
},
{
"key": "entityId",
"value": "{{portfolioId}}"
}
]
}
}
}
]
},
{
"name": "Blogs",
"item": [
{
"name": "List published blogs (website)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set('blogId', json.items[0].id);",
" if (json.items?.[0]?.slug) pm.collectionVariables.set('blogSlug', json.items[0].slug);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/blogs?page=1&pageSize=12",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"blogs"
],
"query": [
{
"key": "page",
"value": "1"
},
{
"key": "pageSize",
"value": "12"
},
{
"key": "type",
"value": "",
"disabled": true
},
{
"key": "categoryId",
"value": "",
"disabled": true
},
{
"key": "title",
"value": "",
"disabled": true
},
{
"key": "tag",
"value": "",
"disabled": true
}
]
}
}
},
{
"name": "Get published blog by slug (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/blogs/{{blogSlug}}"
}
},
{
"name": "List blog comments (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/blogs/{{blogId}}/comments"
}
},
{
"name": "Submit blog comment (website)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.comment?.id) pm.collectionVariables.set('commentId', json.comment.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"authorName\": \"Blog Reader\",\n \"authorEmail\": \"reader@example.com\",\n \"text\": \"Very helpful post!\"\n}"
},
"url": "{{baseUrl}}/tenants/{{domain}}/blogs/{{blogId}}/comments"
}
}
]
},
{
"name": "Videos",
"item": [
{
"name": "List published videos (website)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set('videoId', json.items[0].id);",
" if (json.items?.[0]?.slug) pm.collectionVariables.set('videoSlug', json.items[0].slug);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/videos?page=1&pageSize=12",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"videos"
],
"query": [
{
"key": "page",
"value": "1"
},
{
"key": "pageSize",
"value": "12"
},
{
"key": "provider",
"value": "",
"disabled": true
},
{
"key": "categoryId",
"value": "",
"disabled": true
},
{
"key": "title",
"value": "",
"disabled": true
},
{
"key": "tag",
"value": "",
"disabled": true
}
]
}
}
},
{
"name": "Get published video by slug (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/videos/{{videoSlug}}"
}
},
{
"name": "List video comments (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/videos/{{videoId}}/comments"
}
},
{
"name": "Submit video comment (website)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.comment?.id) pm.collectionVariables.set('commentId', json.comment.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"authorName\": \"Video Viewer\",\n \"authorEmail\": \"viewer@example.com\",\n \"text\": \"Great video!\"\n}"
},
"url": "{{baseUrl}}/tenants/{{domain}}/videos/{{videoId}}/comments"
}
}
]
},
{
"name": "Portfolios",
"item": [
{
"name": "List published portfolios (website)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set('portfolioId', json.items[0].id);",
" if (json.items?.[0]?.slug) pm.collectionVariables.set('portfolioSlug', json.items[0].slug);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"description": "Published portfolios (newest / highest sortOrder first). Items include nullable `projectUrl` (external website link).",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/portfolios?page=1&pageSize=12",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"portfolios"
],
"query": [
{
"key": "page",
"value": "1"
},
{
"key": "pageSize",
"value": "12"
},
{
"key": "categoryId",
"value": "",
"disabled": true
},
{
"key": "title",
"value": "",
"disabled": true
},
{
"key": "tag",
"value": "",
"disabled": true
}
]
}
}
},
{
"name": "Get published portfolio by slug (website)",
"request": {
"method": "GET",
"description": "Portfolio detail. Includes nullable `projectUrl` (external website link).",
"url": "{{baseUrl}}/tenants/{{domain}}/portfolios/{{portfolioSlug}}"
}
},
{
"name": "Get published portfolio by id (website)",
"request": {
"method": "GET",
"description": "Preferred for /portfolio/{slug} pages. Includes nullable `projectUrl`.",
"url": "{{baseUrl}}/tenants/{{domain}}/portfolios/by-id/{{portfolioId}}"
}
},
{
"name": "List portfolio comments (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/portfolios/{{portfolioId}}/comments"
}
},
{
"name": "Submit portfolio comment (website)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.comment?.id) pm.collectionVariables.set('commentId', json.comment.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"authorName\": \"Portfolio Visitor\",\n \"authorEmail\": \"visitor@example.com\",\n \"text\": \"Impressive project!\"\n}"
},
"url": "{{baseUrl}}/tenants/{{domain}}/portfolios/{{portfolioId}}/comments"
}
}
]
},
{
"name": "Expert Reviews",
"item": [
{
"name": "Submit expert review (website)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.review?.id) pm.collectionVariables.set('expertReviewId', json.review.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"productId\": \"{{productId}}\",\n \"authorName\": \"Expert Reviewer\",\n \"authorEmail\": \"expert@example.com\",\n \"rate\": 8,\n \"positivePoints\": [\"Build quality\", \"Great battery life\", \"Comfortable fit\"],\n \"negativePoints\": [\"Pricey\", \"Limited colors\"],\n \"text\": \"Solid product overall with a few trade-offs worth considering.\"\n}"
},
"url": "{{baseUrl}}/tenants/{{domain}}/expert-reviews"
}
},
{
"name": "List approved expert reviews (website)",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/expert-reviews?productId={{productId}}",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"expert-reviews"
],
"query": [
{
"key": "productId",
"value": "{{productId}}"
}
]
}
}
}
]
},
{
"name": "Categories",
"item": [
{
"name": "List product categories",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set('categoryId', json.items[0].id);",
"}"
]
}
}
],
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/categories?entityType=product",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"categories"
],
"query": [
{
"key": "entityType",
"value": "product"
}
]
}
}
},
{
"name": "List blog categories",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/categories?entityType=blog",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"categories"
],
"query": [
{
"key": "entityType",
"value": "blog"
}
]
}
}
},
{
"name": "List portfolio categories",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/categories?entityType=portfolio",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"categories"
],
"query": [
{
"key": "entityType",
"value": "portfolio"
}
]
}
}
}
]
},
{
"name": "Products",
"item": [
{
"name": "List published products",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set('productId', json.items[0].id);",
" if (json.items?.[0]?.slug) pm.collectionVariables.set('productSlug', json.items[0].slug);",
"}"
]
}
}
],
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/products?page=1&pageSize=12",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"products"
],
"query": [
{
"key": "page",
"value": "1"
},
{
"key": "pageSize",
"value": "12"
},
{
"key": "name",
"value": "",
"disabled": true
},
{
"key": "categoryId",
"value": "{{categoryId}}",
"disabled": true
},
{
"key": "brandId",
"value": "{{brandId}}",
"disabled": true
},
{
"key": "tag",
"value": "",
"disabled": true
},
{
"key": "inStore",
"value": "true",
"disabled": true
}
]
}
}
},
{
"name": "Get published product by slug",
"request": {
"method": "GET",
"description": "Product details plus relatedProducts (same category then same brand; in-stock first within each group).",
"url": "{{baseUrl}}/tenants/{{domain}}/products/{{productSlug}}"
}
},
{
"name": "Get product variations by slug",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/products/{{productSlug}}/variations"
}
},
{
"name": "Get product technical info by slug",
"request": {
"method": "GET",
"description": "Field labels + values for the product specs UI. Join form.fields[].id to values[].fieldId. Detail alone has no labels.",
"url": "{{baseUrl}}/tenants/{{domain}}/products/{{productSlug}}/technical-info"
}
}
]
},
{
"name": "User Products",
"item": [
{
"name": "List published user products",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set('userProductId', json.items[0].id);",
" if (json.items?.[0]?.slug) pm.collectionVariables.set('userProductSlug', json.items[0].slug);",
"}"
]
}
}
],
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/user-products?page=1&pageSize=12",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"user-products"
],
"query": [
{
"key": "page",
"value": "1"
},
{
"key": "pageSize",
"value": "12"
},
{
"key": "name",
"value": "",
"disabled": true
},
{
"key": "q",
"value": "",
"disabled": true
},
{
"key": "categoryId",
"value": "{{categoryId}}",
"disabled": true
},
{
"key": "cityId",
"value": "",
"disabled": true
},
{
"key": "countryId",
"value": "",
"disabled": true
},
{
"key": "condition",
"value": "new",
"disabled": true
},
{
"key": "promoted",
"value": "true",
"disabled": true
}
]
}
}
},
{
"name": "Search user products",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/user-products?q=boiler&page=1&pageSize=12",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"user-products"
],
"query": [
{
"key": "q",
"value": "boiler"
},
{
"key": "page",
"value": "1"
},
{
"key": "pageSize",
"value": "12"
}
]
}
}
},
{
"name": "Get user product by slug",
"request": {
"method": "GET",
"description": "Listing detail + gallery. technicalValues are values only (fieldId + text/option) — no labels. Use technical-info for specs UI.",
"url": "{{baseUrl}}/tenants/{{domain}}/user-products/{{userProductSlug}}"
}
},
{
"name": "Get user product technical info by slug",
"request": {
"method": "GET",
"description": "Required for technical-details UI: form.fields (labels) + values. Join field.id ↔ value.fieldId. No separate category-variation-fields public endpoint.",
"url": "{{baseUrl}}/tenants/{{domain}}/user-products/{{userProductSlug}}/technical-info"
}
}
]
},
{
"name": "Store Items",
"item": [
{
"name": "List store item variants",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set('storeItemVariantId', json.items[0].id);",
"}"
]
}
}
],
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/tenants/{{domain}}/store-items?page=1&pageSize=20",
"host": [
"{{baseUrl}}"
],
"path": [
"tenants",
"{{domain}}",
"store-items"
],
"query": [
{
"key": "page",
"value": "1"
},
{
"key": "pageSize",
"value": "20"
},
{
"key": "categoryId",
"value": "",
"disabled": true
},
{
"key": "brandId",
"value": "",
"disabled": true
},
{
"key": "productId",
"value": "{{productId}}",
"disabled": true
},
{
"key": "name",
"value": "",
"disabled": true
},
{
"key": "inStock",
"value": "true",
"disabled": true
},
{
"key": "isFestival",
"value": "true",
"disabled": true
},
{
"key": "minPrice",
"value": "",
"disabled": true
},
{
"key": "maxPrice",
"value": "",
"disabled": true
}
]
}
}
},
{
"name": "Get store item by product ID",
"event": [
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" const variant = json.storeItem?.variants?.[0];",
" if (variant?.id) pm.collectionVariables.set('storeItemVariantId', variant.id);",
"}"
]
}
}
],
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/store-items/by-product/{{productId}}"
}
},
{
"name": "Get store item variant by ID",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/store-items/{{storeItemVariantId}}"
}
}
]
},
{
"name": "Torob",
"item": [
{
"name": "Product API v3 (store module)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "X-Torob-Token",
"value": "{{torobJwt}}"
},
{
"key": "X-Torob-Token-Version",
"value": "1"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"page\": 1,\n \"sort\": \"date_added_desc\"\n}"
},
"url": "{{baseUrl}}/tenants/{{domain}}/torob_api/v3/products",
"description": "Torob-only. 404 if the tenant does not have the store module. Prefer the shop-domain URL POST https://{domain}/torob_api/v3/products after nginx is patched."
}
}
]
},
{
"name": "Homepage",
"item": [
{
"name": "Get business info (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/website/business-info"
}
},
{
"name": "Get favicon + logos (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/website/favicon"
}
},
{
"name": "List category groups (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/website/category-groups"
}
},
{
"name": "List brand groups (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/website/brand-groups"
}
},
{
"name": "List sliders (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/website/sliders"
}
},
{
"name": "List static images (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/website/static-images?pageKey=home"
}
},
{
"name": "Get static image slot by key (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/website/static-images/home-hero"
}
},
{
"name": "List store specials (website)",
"request": {
"method": "GET",
"url": "{{baseUrl}}/tenants/{{domain}}/store-specials"
}
}
]
},
{
"name": "Cart",
"item": [
{
"name": "Get cart",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": "{{baseUrl}}/businesses/{{businessId}}/cart"
}
},
{
"name": "Add variant to cart",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" const items = json.cart?.items || [];",
" if (items.length) pm.collectionVariables.set('cartItemId', items[items.length - 1].id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"storeItemVariantId\": \"{{storeItemVariantId}}\",\n \"quantity\": 1\n}"
},
"url": "{{baseUrl}}/businesses/{{businessId}}/cart/items"
}
},
{
"name": "Update cart item quantity",
"request": {
"method": "PATCH",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"quantity\": 2\n}"
},
"url": "{{baseUrl}}/businesses/{{businessId}}/cart/items/{{cartItemId}}"
}
},
{
"name": "Remove cart item",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": "{{baseUrl}}/businesses/{{businessId}}/cart/items/{{cartItemId}}"
}
},
{
"name": "Clear cart",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": "{{baseUrl}}/businesses/{{businessId}}/cart"
}
},
{
"name": "Checkout cart (cash payment)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.order?.id) pm.collectionVariables.set('orderId', json.order.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"shippingAddress\": {\n \"province\": \"Tehran\",\n \"city\": \"Tehran\",\n \"address\": \"123 Example Street\",\n \"postalCode\": \"1234567890\",\n \"landline\": \"02112345678\"\n },\n \"customerNotes\": \"Please call before delivery\",\n \"payment\": {\n \"type\": \"cash\"\n }\n}"
},
"url": "{{baseUrl}}/businesses/{{businessId}}/cart/checkout"
}
},
{
"name": "Checkout cart (e-payment / Mellat)",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200 || pm.response.code === 201) {",
" const json = pm.response.json();",
" if (json.order?.id) pm.collectionVariables.set('orderId', json.order.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"addressId\": \"{{addressId}}\",\n \"customerNotes\": \"Deliver after 5pm\",\n \"returnUrl\": \"https://YOUR_WEBSITE_DOMAIN/checkout/result\",\n \"payment\": {\n \"type\": \"e_payment_gate\",\n \"gatewayType\": \"mellat\"\n }\n}"
},
"url": "{{baseUrl}}/businesses/{{businessId}}/cart/checkout",
"description": "Creates a pending order + transaction, calls the gateway, and returns payment.redirect { method, url, fields }. Website must POST/redirect the shopper to the bank. After payment, the bank hits https://YOUR_WEBSITE_DOMAIN/meshkee/payments/{gateway}/callback (nginx → API), which redirects to returnUrl?status=success|failed."
}
}
]
},
{
"name": "Payments",
"item": [
{
"name": "List payment methods",
"request": {
"method": "GET",
"header": [],
"url": "{{baseUrl}}/businesses/{{businessId}}/payments/methods",
"description": "Public. Returns enabled gateways (no secrets). Also available on GET /tenants/{host} as ePayment."
}
},
{
"name": "Mellat callback via store domain (gateway → nginx → API)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/x-www-form-urlencoded"
}
],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "ResCode",
"value": "0"
},
{
"key": "SaleOrderId",
"value": "{{transactionId}}"
},
{
"key": "SaleReferenceId",
"value": "123456789"
},
{
"key": "RefId",
"value": "{{refId}}"
}
]
},
"url": "https://YOUR_WEBSITE_DOMAIN/meshkee/payments/mellat/callback",
"description": "Preferred path: bank posts to the store apex; nginx proxies to /tenants/{host}/payments/mellat/callback. Verifies+settles, then 303 redirect to returnUrl."
}
},
{
"name": "ZarinPal callback via store domain (gateway → nginx → API)",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "https://YOUR_WEBSITE_DOMAIN/meshkee/payments/zarinpal/callback?Authority={{authority}}&Status=OK",
"host": ["YOUR_WEBSITE_DOMAIN"],
"path": [
"meshkee",
"payments",
"zarinpal",
"callback"
],
"query": [
{ "key": "Authority", "value": "{{authority}}" },
{ "key": "Status", "value": "OK" }
]
},
"description": "Preferred path: ZarinPal redirects to the store apex; nginx proxies to /tenants/{host}/payments/zarinpal/callback. Verifies payment, then 303 redirect to returnUrl."
}
},
{
"name": "Mellat callback (legacy API URL)",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/x-www-form-urlencoded"
}
],
"body": {
"mode": "urlencoded",
"urlencoded": [
{
"key": "ResCode",
"value": "0"
},
{
"key": "SaleOrderId",
"value": "{{transactionId}}"
},
{
"key": "SaleReferenceId",
"value": "123456789"
},
{
"key": "RefId",
"value": "{{refId}}"
}
]
},
"url": "{{baseUrl}}/businesses/{{businessId}}/payments/mellat/callback",
"description": "Legacy direct API callback. Prefer store-domain /meshkee/payments/... so gateway domain checks match the merchant terminal."
}
},
{
"name": "ZarinPal callback (legacy API URL)",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{baseUrl}}/businesses/{{businessId}}/payments/zarinpal/callback?Authority={{authority}}&Status=OK",
"host": ["{{baseUrl}}"],
"path": [
"businesses",
"{{businessId}}",
"payments",
"zarinpal",
"callback"
],
"query": [
{ "key": "Authority", "value": "{{authority}}" },
{ "key": "Status", "value": "OK" }
]
},
"description": "Legacy direct API callback. Prefer store-domain /meshkee/payments/... so ZarinPal domain checks match the merchant terminal."
}
}
]
},
{
"name": "Orders",
"item": [
{
"name": "List my orders",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": {
"raw": "{{baseUrl}}/businesses/{{businessId}}/orders?page=1&pageSize=20",
"host": [
"{{baseUrl}}"
],
"path": [
"businesses",
"{{businessId}}",
"orders"
],
"query": [
{
"key": "page",
"value": "1"
},
{
"key": "pageSize",
"value": "20"
}
]
}
}
},
{
"name": "Get my order",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": "{{baseUrl}}/businesses/{{businessId}}/orders/{{orderId}}"
}
}
]
},
{
"name": "Contact",
"item": [
{
"name": "Submit contact form",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"title\": \"Product inquiry\",\n \"name\": \"Website Visitor\",\n \"email\": \"visitor@example.com\",\n \"cellNumber\": \"+989121234567\",\n \"text\": \"I would like more information about your products.\"\n}"
},
"url": "{{baseUrl}}/tenants/{{domain}}/contact-submissions"
}
}
]
},
{
"name": "Favorites",
"item": [
{
"name": "List favorites",
"request": {
"method": "GET",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": {
"raw": "{{baseUrl}}/businesses/{{businessId}}/favorites?page=1&pageSize=20",
"host": [
"{{baseUrl}}"
],
"path": [
"businesses",
"{{businessId}}",
"favorites"
],
"query": [
{
"key": "page",
"value": "1"
},
{
"key": "pageSize",
"value": "20"
}
]
}
}
},
{
"name": "Add favorite",
"request": {
"method": "POST",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
},
{
"key": "Content-Type",
"value": "application/json"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"productId\": \"{{productId}}\"\n}"
},
"url": "{{baseUrl}}/businesses/{{businessId}}/favorites"
}
},
{
"name": "Remove favorite",
"request": {
"method": "DELETE",
"header": [
{
"key": "Authorization",
"value": "Bearer {{accessToken}}"
}
],
"url": "{{baseUrl}}/businesses/{{businessId}}/favorites/{{productId}}"
}
}
]
},
{
"name": "Cities (address forms)",
"item": [
{
"name": "List countries",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set('countryId', json.items[0].id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/cities?level=country",
"host": [
"{{baseUrl}}"
],
"path": [
"cities"
],
"query": [
{
"key": "level",
"value": "country"
}
]
}
}
},
{
"name": "List Iran provinces",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" if (json.items?.[0]?.id) pm.collectionVariables.set('provinceId', json.items[0].id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/cities?level=province&parentSlug=iran",
"host": [
"{{baseUrl}}"
],
"path": [
"cities"
],
"query": [
{
"key": "level",
"value": "province"
},
{
"key": "parentSlug",
"value": "iran"
}
]
}
}
},
{
"name": "List cities in Tehran province",
"event": [
{
"listen": "test",
"script": {
"exec": [
"if (pm.response.code === 200) {",
" const json = pm.response.json();",
" const tehran = json.items?.find((item) => item.slug === 'tehran');",
" if (tehran?.id) pm.collectionVariables.set('cityId', tehran.id);",
"}"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/cities?level=city&parentSlug=tehran-province",
"host": [
"{{baseUrl}}"
],
"path": [
"cities"
],
"query": [
{
"key": "level",
"value": "city"
},
{
"key": "parentSlug",
"value": "tehran-province"
}
]
}
}
},
{
"name": "List cities by province ID",
"request": {
"method": "GET",
"url": {
"raw": "{{baseUrl}}/cities?level=city&parentId={{provinceId}}",
"host": [
"{{baseUrl}}"
],
"path": [
"cities"
],
"query": [
{
"key": "level",
"value": "city"
},
{
"key": "parentId",
"value": "{{provinceId}}"
}
]
}
}
},
{
"name": "Get city by ID",
"request": {
"method": "GET",
"url": "{{baseUrl}}/cities/{{cityId}}"
}
}
]
},
{
"name": "Partner SMS",
"description": "Server-to-server SMS for allowlisted partner domains. Set collection variable `smsApiKey`. Docs: https://api.meshkee.com/docs/website/SMS.md",
"item": [
{
"name": "Send SMS",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
},
{
"key": "X-Api-Key",
"value": "{{smsApiKey}}"
}
],
"body": {
"mode": "raw",
"raw": "{\n \"domain\": \"baloutpastry.com\",\n \"to\": \"09127004945\",\n \"message\": \"سفارش شما به شماره ی ۱۲۱۱۳۲۲ اماده می باشد.\"\n}"
},
"url": "{{baseUrl}}/public/sms/send",
"description": "Requires allowlisted domain + matching API key. Not for browser JS."
}
}
]
}
]
}