{
  "info": {
    "_postman_id": "5d0cb2095d0cb2095d0cb209",
    "name": "KamTeswa API",
    "description": "# KamTeswa API\n\nProfessional API collection for the KamTeswa marketplace platform.\n\n## Base URL\n`{{baseUrl}}`\n\nDelivery default: `https://dashboard.kam-teswa.4hoste.com/api`\n\nRequest paths are relative to that base, for example:\n- `{{baseUrl}}/client/signup`\n- `{{baseUrl}}/provider/signup`\n- `{{baseUrl}}/signin` for both client and provider login, selected by `userType`\n\n## Authentication\nMost endpoints require:\n- `secretkey: {{secretKey}}` (OpenAPI **SecretKeyAuth**)\n\nSet `{{secretKey}}` before running protected or visitor endpoints.\n\nClient endpoints may also require:\n- `Authorization: Bearer {{clientToken}}`\n\nProvider endpoints may also require:\n- `Authorization: Bearer {{providerToken}}`\n\n**Header name note:** the live API and OpenAPI scheme use `secretkey`, not `x-secret-key`.\n\n## Response Envelope\nAll standard API responses follow:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n\n## Getting Started\n1. Select the **KamTeswa Delivery** environment.\n2. Set `secretKey` by replacing `PUT_SECRET_KEY_HERE` with the approved environment value.\n3. Run **Shared Auth → Sign in** (`POST /signin`) with `userType=client` or `provider`.\n4. Token is saved automatically **only** from token-returning login (`POST /signin`).\n5. Run protected requests.\n\n## Pagination\n- Query parameters: `page` + `limit={{perPage}}`\n- Response may include `paginate.perPage`\n\n## Security\nDo not commit real tokens or production secrets.\nDo not log passwords, OTP codes, or bearer tokens in Postman scripts.\n\n## Source\nGenerated from `public/api-docs/openapi.json` via `npm run postman:export`.\n",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Safe request defaults. Values from the selected environment take precedence.",
          "if (!pm.variables.get(\"locale\")) {",
          "  pm.collectionVariables.set(\"locale\", \"ar\");",
          "}",
          "if (!pm.variables.get(\"page\")) {",
          "  pm.collectionVariables.set(\"page\", \"1\");",
          "}",
          "if (!pm.variables.get(\"perPage\")) {",
          "  pm.collectionVariables.set(\"perPage\", \"10\");",
          "}",
          ""
        ]
      }
    }
  ],
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://dashboard.kam-teswa.4hoste.com/api",
      "type": "string",
      "description": "API base including /api. Requests use {{baseUrl}}/client/signup style paths."
    },
    {
      "key": "secretKey",
      "value": "PUT_SECRET_KEY_HERE",
      "type": "string",
      "description": "Platform secret for SecretKeyAuth. Sent as header secretkey. Replace before use. Never commit real production secrets."
    },
    {
      "key": "visitorToken",
      "value": "",
      "type": "string",
      "description": "Reserved empty placeholder. No visitor-token endpoint exists, so no request or script uses this value."
    },
    {
      "key": "clientToken",
      "value": "",
      "type": "string",
      "description": "Filled automatically by POST /signin when userType=client and key=success."
    },
    {
      "key": "providerToken",
      "value": "",
      "type": "string",
      "description": "Filled automatically by POST /signin when userType=provider and key=success."
    },
    {
      "key": "clientPhone",
      "value": "0512345678",
      "type": "string",
      "description": "Sample client phone (local part)."
    },
    {
      "key": "clientCountryCode",
      "value": "+966",
      "type": "string",
      "description": "Sample client country code."
    },
    {
      "key": "clientEmail",
      "value": "client@example.com",
      "type": "string",
      "description": "Sample client email."
    },
    {
      "key": "clientPassword",
      "value": "123456789@aA",
      "type": "string",
      "description": "Sample client password for local/QA only."
    },
    {
      "key": "providerPhone",
      "value": "0551234567",
      "type": "string",
      "description": "Sample provider phone (local part)."
    },
    {
      "key": "providerCountryCode",
      "value": "+966",
      "type": "string",
      "description": "Sample provider country code."
    },
    {
      "key": "providerEmail",
      "value": "provider@example.com",
      "type": "string",
      "description": "Sample provider email."
    },
    {
      "key": "providerPassword",
      "value": "123456789@aA",
      "type": "string",
      "description": "Sample provider password for local/QA only."
    },
    {
      "key": "otpCode",
      "value": "PUT_OTP_CODE_HERE",
      "type": "string",
      "description": "Enter the current QA OTP when testing a verification flow. Never commit or log real OTPs."
    },
    {
      "key": "clientId",
      "value": "",
      "type": "string",
      "description": "Optionally set from signin data.id (client)."
    },
    {
      "key": "providerId",
      "value": "",
      "type": "string",
      "description": "Optionally set from signin data.id (provider)."
    },
    {
      "key": "productId",
      "value": "",
      "type": "string",
      "description": "Product ObjectId for product-scoped requests."
    },
    {
      "key": "productAttributeId",
      "value": "6a64b113dc6f51f903524f5a",
      "type": "string",
      "description": "Sample Product Attribute MongoId (e.g. الحجم). Use in attributes JSON."
    },
    {
      "key": "productAttributeValueId",
      "value": "6a64b145dc6f51f903524f73",
      "type": "string",
      "description": "Sample AttributeValue MongoId under productAttributeId."
    },
    {
      "key": "sizeAttributeId",
      "value": "6a64b113dc6f51f903524f5a",
      "type": "string",
      "description": "Sample size attribute MongoId for variant products."
    },
    {
      "key": "sizeValueId",
      "value": "6a64b145dc6f51f903524f73",
      "type": "string",
      "description": "Sample size value MongoId (e.g. XL)."
    },
    {
      "key": "colorAttributeId",
      "value": "6a64b113dc6f51f903524f5b",
      "type": "string",
      "description": "Sample color attribute MongoId for variant products."
    },
    {
      "key": "colorValueId",
      "value": "6a64b15fdc6f51f903524f8d",
      "type": "string",
      "description": "Sample color value MongoId (e.g. أحمر)."
    },
    {
      "key": "orderId",
      "value": "",
      "type": "string",
      "description": "Order ObjectId when needed by a request."
    },
    {
      "key": "categoryId",
      "value": "",
      "type": "string",
      "description": "Category / attribute-related ObjectId when needed."
    },
    {
      "key": "departmentId",
      "value": "",
      "type": "string",
      "description": "Department ObjectId when needed."
    },
    {
      "key": "cityId",
      "value": "",
      "type": "string",
      "description": "City ObjectId from lookups."
    },
    {
      "key": "auctionId",
      "value": "",
      "type": "string",
      "description": "Auction ObjectId when needed."
    },
    {
      "key": "notificationId",
      "value": "",
      "type": "string",
      "description": "Notification ObjectId for delete/detail flows."
    },
    {
      "key": "page",
      "value": "1",
      "type": "string",
      "description": "List page query (OpenAPI: page)."
    },
    {
      "key": "perPage",
      "value": "10",
      "type": "string",
      "description": "Page-size value. Exported requests send this variable through the real query name `limit`."
    },
    {
      "key": "locale",
      "value": "ar",
      "type": "string",
      "description": "lang header value: ar | en."
    }
  ],
  "item": [
    {
      "name": "00 — Public / Visitor",
      "id": "0b8450da0b8450da0b8450da",
      "description": "KamTeswa API — 00 — Public / Visitor",
      "item": [
        {
          "name": "Change device response language",
          "id": "09bed17109bed17109bed171",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/language",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "language"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nChange device response language\n\nChanges the saved language for the supplied device. The actual endpoint is\ndevice-scoped: `lang` comes from the required header and `deviceId` comes from\nthe multipart form body.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `deviceId`\nStable device identifier used for language and session context.\nRequired.\nType: string.\nExample: current-device-id\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Device language changed successfully.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "deviceId",
                  "type": "text",
                  "description": "Stable device identifier used for language and session context.\nRequired.\nType: string.\nExample: current-device-id",
                  "disabled": false,
                  "value": "current-device-id"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List push-notification keys",
          "id": "2109a2712109a2712109a271",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notification-keys",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notification-keys"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nList push-notification keys\n\n- Public static catalogue of push-notification keys and their client-side actions (no user data involved).\n- Actual route name is `/notification-keys` (not `/notifications/key-notifications`).\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Push-notification key catalogue.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "01 — Client Auth",
      "id": "540217825402178254021782",
      "description": "KamTeswa API — 01 — Client Auth",
      "item": [
        {
          "name": "Register a new client account",
          "id": "02a6fb8d02a6fb8d02a6fb8d",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/client/signup",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "client",
                "signup"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nRegister a new client account\n\n- Creates an inactive client account.\n- Sends an OTP by SMS and never returns it.\n- The next step is `PATCH /activate` with purpose `activation`.\n- Unknown fields are ignored; only validated fields are consumed.\n\n## Actor\nVisitor / Client\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `name`\nDisplay name for the account or contact.\nRequired.\nType: string.\nExample: Example Client\n\n### `countryCode`\nPhone country code including the plus sign.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}\n\n### `phone`\nLocal phone number without country code. Must be unique when registering.\nRequired.\nType: string.\nExample variable: {{clientPhone}}\n\n### `email`\nEmail address for the account or contact.\nOptional.\nType: string.\nExample variable: {{clientEmail}}\n\n### `password`\nAccount password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.\n\n### `confirmPassword`\nMust match password.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.\n\n### `avatar`\nOptional profile image. Allowed formats (validated by file signature): jpg, jpeg, png, webp.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Account created; OTP activation is required.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `needActive`.\n- Notable `data` fields (from docs examples): `id`, `name`, `avatar`, `countryCode`, `phone`, `fullPhone`, `email`, `userType`, `status`, `statusText`, `notifyCount`, `isNotify`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "name",
                  "type": "text",
                  "description": "Display name for the account or contact.\nRequired.\nType: string.\nExample: Example Client",
                  "disabled": false,
                  "value": "Example Client"
                },
                {
                  "key": "countryCode",
                  "type": "text",
                  "description": "Phone country code including the plus sign.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}",
                  "disabled": false,
                  "value": "{{clientCountryCode}}"
                },
                {
                  "key": "phone",
                  "type": "text",
                  "description": "Local phone number without country code. Must be unique when registering.\nRequired.\nType: string.\nExample variable: {{clientPhone}}",
                  "disabled": false,
                  "value": "{{clientPhone}}"
                },
                {
                  "key": "email",
                  "type": "text",
                  "description": "Email address for the account or contact.\nOptional.\nType: string.\nExample variable: {{clientEmail}}",
                  "disabled": false,
                  "value": "{{clientEmail}}"
                },
                {
                  "key": "password",
                  "type": "text",
                  "description": "Account password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{clientPassword}}"
                },
                {
                  "key": "confirmPassword",
                  "type": "text",
                  "description": "Must match password.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{clientPassword}}"
                },
                {
                  "key": "avatar",
                  "type": "file",
                  "description": "Optional profile image. Allowed formats (validated by file signature): jpg, jpeg, png, webp.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "02 — Provider Auth",
      "id": "235b3fd9235b3fd9235b3fd9",
      "description": "KamTeswa API — 02 — Provider Auth",
      "item": [
        {
          "name": "Submit a provider account request",
          "id": "4c70d5994c70d5994c70d599",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/provider/signup",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "provider",
                "signup"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSubmit a provider account request\n\n- Creates matching Provider and ProviderMeta records with the same ID, active false, and approvalStatus wait.\n- Sends the existing account-activation OTP without returning it.\n- Password hashing is handled by the existing User model save hook.\n- Optional files are validated by signature before storage.\n- Unknown body fields are rejected.\n\n## Actor\nVisitor / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `avatar`\nOptional profile image. Allowed types: jpg, jpeg, png, webp.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\n### `name`\nDisplay name for the account or contact.\nRequired.\nType: string.\nExample: Example Provider\n\n### `countryCode`\nPhone country code including the plus sign.\nRequired.\nType: string.\nExample variable: {{providerCountryCode}}\n\n### `phone`\nLocal phone number without country code. Must be unique when registering.\nRequired.\nType: string.\nExample variable: {{providerPhone}}\n\n### `email`\nEmail address for the account or contact.\nOptional.\nType: string.\nExample variable: {{providerEmail}}\n\n### `password`\nAccount password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{providerPassword}}\nNever returned by the API. Do not log this value.\n\n### `confirmPassword`\nMust match password.\nRequired.\nType: string.\nExample variable: {{providerPassword}}\nNever returned by the API. Do not log this value.\n\n### `nationalId`\nOptional provider national or residency ID.\nOptional.\nType: string.\nExample: 1000000000\n\n### `city`\nActive and visible City document ID.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34cf\n\n### `commercialRegisterImage`\nOptional commercial-register files. Maximum 3. Allowed types: jpg, jpeg, png, pdf.\nOptional.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.\n\n### `whatsappCountryCode`\nWhatsapp Country Code field.\nOptional.\nType: string.\nExample: +966\n\n### `whatsappNumber`\nWhatsapp Number field.\nRequired.\nType: string.\nExample: 0551234567\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Provider account request received; OTP activation is required.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `needActive`.\n- Notable `data` fields (from docs examples): `id`, `avatar`, `name`, `countryCode`, `phone`, `fullPhone`, `city`, `userType`, `status`, `statusText`, `notifyCount`, `isNotify`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "avatar",
                  "type": "file",
                  "description": "Optional profile image. Allowed types: jpg, jpeg, png, webp.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "name",
                  "type": "text",
                  "description": "Display name for the account or contact.\nRequired.\nType: string.\nExample: Example Provider",
                  "disabled": false,
                  "value": "Example Provider"
                },
                {
                  "key": "countryCode",
                  "type": "text",
                  "description": "Phone country code including the plus sign.\nRequired.\nType: string.\nExample variable: {{providerCountryCode}}",
                  "disabled": false,
                  "value": "{{providerCountryCode}}"
                },
                {
                  "key": "phone",
                  "type": "text",
                  "description": "Local phone number without country code. Must be unique when registering.\nRequired.\nType: string.\nExample variable: {{providerPhone}}",
                  "disabled": false,
                  "value": "{{providerPhone}}"
                },
                {
                  "key": "email",
                  "type": "text",
                  "description": "Email address for the account or contact.\nOptional.\nType: string.\nExample variable: {{providerEmail}}",
                  "disabled": false,
                  "value": "{{providerEmail}}"
                },
                {
                  "key": "password",
                  "type": "text",
                  "description": "Account password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{providerPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{providerPassword}}"
                },
                {
                  "key": "confirmPassword",
                  "type": "text",
                  "description": "Must match password.\nRequired.\nType: string.\nExample variable: {{providerPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{providerPassword}}"
                },
                {
                  "key": "nationalId",
                  "type": "text",
                  "description": "Optional provider national or residency ID.\nOptional.\nType: string.\nExample: 1000000000",
                  "disabled": false,
                  "value": "1000000000"
                },
                {
                  "key": "city",
                  "type": "text",
                  "description": "Active and visible City document ID.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34cf",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34cf"
                },
                {
                  "key": "commercialRegisterImage",
                  "type": "file",
                  "description": "Optional commercial-register files. Maximum 3. Allowed types: jpg, jpeg, png, pdf.\nOptional.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "whatsappCountryCode",
                  "type": "text",
                  "description": "Whatsapp Country Code field.\nOptional.\nType: string.\nExample: +966",
                  "disabled": false,
                  "value": "+966"
                },
                {
                  "key": "whatsappNumber",
                  "type": "text",
                  "description": "Whatsapp Number field.\nRequired.\nType: string.\nExample: 0551234567",
                  "disabled": false,
                  "value": "0551234567"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "03 — Shared Auth",
      "id": "763fab5a763fab5a763fab5a",
      "description": "KamTeswa API — 03 — Shared Auth",
      "item": [
        {
          "name": "Sign in as client or provider",
          "id": "4f0522ef4f0522ef4f0522ef",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/signin",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "signin"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSign in as client or provider\n\n- Verifies `countryCode + phone + password`.\n- `userType` selects exactly `client` or `provider`; identities are not mixed.\n- For an AccountIdentity-backed profile, the single canonical\n  `AccountIdentity.password` is verified for both Client and Provider modes.\n  A stale legacy profile password never overrides it.\n- Legacy profiles without AccountIdentity keep their existing password behavior.\n- A successful JWT contains the stored `userType` claim.\n- Inactive accounts receive a new OTP and a `needActive` response without a token.\n- Blocked accounts and providers awaiting/rejected by admin are refused.\n- Password and OTP values are never returned or logged.\n\n**Account password policy:** AccountIdentity-backed Client and Provider profiles verify one canonical AccountIdentity password; unlinked legacy profiles keep legacy verification.\n\n**Account token policy:** Tokens are opaque to clients. While AUTH_ACCOUNT_IDENTITY_TOKEN_ENABLED is false, legacy profile-subject tokens remain unchanged. When enabled, a linked active account receives an AccountIdentity-subject token whose current Client/Provider profile is resolved from DB activeMode.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `phone`\nValid phone number for countryCode; normalized before lookup.\nRequired.\nType: string.\nExample variable: {{clientPhone}}\n\n### `countryCode`\nValid international calling code.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}\n\n### `userType`\nUser type; selects the exact identity model:\n- `client`: mobile client/customer.\n- `provider`: service provider/store owner.\nRequired.\nType: string.\nAllowed values: client, provider.\nExample: client\n\n### `password`\nAccount password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.\n\n### `deviceId`\nStable device identifier used for language and session context.\nRequired.\nType: string.\nExample: client-device-installation-id\n\n### `deviceType`\nDevice platform:\n- `android`: Android application.\n- `ios`: iOS application.\n- `web`: Web client.\nRequired.\nType: string.\nAllowed values: android, ios, web.\nExample: android\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Login succeeded, or the account requires OTP activation.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `name`, `countryCode`, `phone`, `fullPhone`, `userType`, `status`, `active`, `token`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "phone",
                  "type": "text",
                  "description": "Valid phone number for countryCode; normalized before lookup.\nRequired.\nType: string.\nExample variable: {{clientPhone}}",
                  "disabled": false,
                  "value": "{{clientPhone}}"
                },
                {
                  "key": "countryCode",
                  "type": "text",
                  "description": "Valid international calling code.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}",
                  "disabled": false,
                  "value": "{{clientCountryCode}}"
                },
                {
                  "key": "userType",
                  "type": "text",
                  "description": "User type; selects the exact identity model:\n- `client`: mobile client/customer.\n- `provider`: service provider/store owner.\nRequired.\nType: string.\nAllowed values: client, provider.\nExample: client",
                  "disabled": false,
                  "value": "client"
                },
                {
                  "key": "password",
                  "type": "text",
                  "description": "Account password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{clientPassword}}"
                },
                {
                  "key": "deviceId",
                  "type": "text",
                  "description": "Stable device identifier used for language and session context.\nRequired.\nType: string.\nExample: client-device-installation-id",
                  "disabled": false,
                  "value": "client-device-installation-id"
                },
                {
                  "key": "deviceType",
                  "type": "text",
                  "description": "Device platform:\n- `android`: Android application.\n- `ios`: iOS application.\n- `web`: Web client.\nRequired.\nType: string.\nAllowed values: android, ios, web.\nExample: android",
                  "disabled": false,
                  "value": "android"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  "",
                  "",
                  "const token =",
                  "  (json && json.data && typeof json.data.token === \"string\" && json.data.token) ||",
                  "  null;",
                  "",
                  "if (json.key === \"success\") {",
                  "  pm.test(\"Token exists on successful signin\", function () {",
                  "    pm.expect(token).to.be.a(\"string\").and.not.empty;",
                  "  });",
                  "}",
                  "",
                  "if (json.key === \"success\" && typeof token === \"string\" && token.length > 0) {",
                  "  let userType = null;",
                  "  const body = pm.request.body;",
                  "  if (body && body.mode === \"formdata\" && body.formdata) {",
                  "    const row = body.formdata.find((f) => f.key === \"userType\");",
                  "    userType = row && row.value;",
                  "  } else if (body && body.mode === \"urlencoded\" && body.urlencoded) {",
                  "    const row = body.urlencoded.find((f) => f.key === \"userType\");",
                  "    userType = row && row.value;",
                  "  } else if (body && body.mode === \"raw\") {",
                  "    try {",
                  "      const rawBody = JSON.parse(body.raw || \"{}\");",
                  "      userType = rawBody.userType || null;",
                  "    } catch (e) {}",
                  "  }",
                  "  if (!userType && json.data && json.data.userType) {",
                  "    userType = json.data.userType;",
                  "  }",
                  "",
                  "  if (userType === \"client\") {",
                  "    pm.environment.set(\"clientToken\", token);",
                  "    if (json.data && json.data.id) pm.environment.set(\"clientId\", String(json.data.id));",
                  "  } else if (userType === \"provider\") {",
                  "    pm.environment.set(\"providerToken\", token);",
                  "    if (json.data && json.data.id) pm.environment.set(\"providerId\", String(json.data.id));",
                  "  }",
                  "}",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Verify OTP code",
          "id": "0c458fd60c458fd60c458fd6",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Optional Bearer token. Disabled by default for guest access (SecretKeyAuth alone). Enable and use {{clientToken}} or {{providerToken}} when calling as an authenticated actor.",
                "disabled": true
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/activate",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "activate"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nVerify OTP code\n\n- Verifies the OTP, its expiry, the selected `userType`, and the current auth flow.\n- `purpose: activation` activates the account and may issue a normal JWT.\n- `purpose: forgot_password` does not change the password or activation state.\n  For AccountIdentity-backed users, the OTP and reset-session hashes belong to\n  AccountIdentity and are shared across Client/Provider roles.\n- `purpose: change_phone` requires an authenticated client/provider and updates the\n  pending phone only after successful OTP verification. Send the new value as\n  `phone`; the `changePhoneToken` was already validated by `PATCH /change-phone`\n  and is not submitted again. The response returns the complete safe client or\n  provider profile object according to the authenticated account type.\n- Forgot-password verification returns a 10-minute opaque reset token; only its hash is stored.\n- The OTP is invalidated on success and is never returned.\n- Three invalid attempts temporarily block further verification in the running process.\n\n**Account password policy:** Forgot-password OTP and reset-session hashes belong to AccountIdentity for linked profiles; activation and unlinked legacy flows retain their existing storage.\n\n**Account token policy:** Successful activation may issue the same feature-flagged AccountIdentity token as sign-in. Pending providers never receive operational provider access, and legacy token behavior remains available while the flag is off.\n\n## Actor\nVisitor / Client / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** with SecretKeyAuth for guests.\n- Optional **ClientBearerAuth** or **ProviderBearerAuth**.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: optional Bearer token when calling as an authenticated actor\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `userType`\nRequired for activation and forgot_password; omitted for authenticated change_phone.\nOptional.\nType: string.\nAllowed values: client, provider.\n\n### `countryCode`\nCurrent country code for public flows; pending new country code for change_phone.\nOptional.\nType: string.\nExample variable: {{clientCountryCode}}\n\n### `phone`\nRequired for activation and forgot_password. For authenticated\nchange_phone, send the new phone and it must match the pending phone\nsaved by PATCH /change-phone.\nOptional.\nType: string.\nExample variable: {{clientPhone}}\n\n### `code`\nVerification code sent by SMS. It is never returned in an API response.\nRequired.\nType: string.\nExample variable: {{otpCode}}\nOTP is never returned by the API.\n\n### `purpose`\n- `activation`: public account activation; deviceId and deviceType are required.\n- `forgot_password`: public recovery verification and reset-session creation.\n- `change_phone`: authenticated pending-phone verification.\nRequired.\nType: string.\nAllowed values: activation, forgot_password, change_phone.\nExample: change_phone\n\n### `deviceId`\nRequired only when purpose is activation.\nOptional.\nType: string.\n\n### `deviceType`\nRequired only when purpose is activation.\nOptional.\nType: string.\nAllowed values: android, ios, web.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Account activated, reset session created, or phone updated with the complete client/provider profile returned.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `userType`, `expiresIn`, `purpose`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "userType",
                  "type": "text",
                  "description": "Required for activation and forgot_password; omitted for authenticated change_phone.\nOptional.\nType: string.\nAllowed values: client, provider.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "countryCode",
                  "type": "text",
                  "description": "Current country code for public flows; pending new country code for change_phone.\nOptional.\nType: string.\nExample variable: {{clientCountryCode}}",
                  "disabled": false,
                  "value": "{{clientCountryCode}}"
                },
                {
                  "key": "phone",
                  "type": "text",
                  "description": "Required for activation and forgot_password. For authenticated\nchange_phone, send the new phone and it must match the pending phone\nsaved by PATCH /change-phone.\nOptional.\nType: string.\nExample variable: {{clientPhone}}",
                  "disabled": false,
                  "value": "{{clientPhone}}"
                },
                {
                  "key": "code",
                  "type": "text",
                  "description": "Verification code sent by SMS. It is never returned in an API response.\nRequired.\nType: string.\nExample variable: {{otpCode}}\nOTP is never returned by the API.",
                  "disabled": false,
                  "value": "{{otpCode}}"
                },
                {
                  "key": "purpose",
                  "type": "text",
                  "description": "- `activation`: public account activation; deviceId and deviceType are required.\n- `forgot_password`: public recovery verification and reset-session creation.\n- `change_phone`: authenticated pending-phone verification.\nRequired.\nType: string.\nAllowed values: activation, forgot_password, change_phone.\nExample: change_phone",
                  "disabled": false,
                  "value": "change_phone"
                },
                {
                  "key": "deviceId",
                  "type": "text",
                  "description": "Required only when purpose is activation.\nOptional.\nType: string.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "deviceType",
                  "type": "text",
                  "description": "Required only when purpose is activation.\nOptional.\nType: string.\nAllowed values: android, ios, web.",
                  "disabled": false,
                  "value": ""
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Save client or provider location",
          "id": "3841c5143841c5143841c514",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/location",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "location"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSave client or provider location\n\nSave the selected coordinates as the account's GeoJSON location.\n\n- With a selected Client/Provider bearer token, `optionalAuth` identifies that account and `countryCode`/`phone` may be omitted.\n- Without a bearer token, the current fallback supports a provider awaiting approval and requires `countryCode` plus `phone`.\n- The mobile application may send `titleAr`, `titleEn`, `descriptionAr`, and `descriptionEn`.\n- The response returns `location.title` and `location.description` in the language selected by the `lang` header.\n- `userType` is not a request field; the verified bearer token or provider lookup determines the account type.\n- A client or approved provider receives an access token; a pending provider receives `wait_approval` without a usable access token.\n- `SecretKeyAuth` is the only API security requirement for this public onboarding step.\n- Swagger UI offers `No bearer token`, `Client token`, and `Provider token`; it sends only the selected credential.\n- Passwords and OTP values are never returned.\n\n**Account token policy:** When this flow must issue a new token, it follows the same gated AccountIdentity/legacy policy as sign-in. An existing bearer remains opaque and is reused; pending providers receive no operational token.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `countryCode`\nRequired with `phone` only when no bearer token is selected (pending-provider flow).\nOptional.\nType: string.\nExample variable: {{clientCountryCode}}\n\n### `phone`\nRequired with `countryCode` only when no bearer token is selected (pending-provider flow).\nOptional.\nType: string.\nExample variable: {{clientPhone}}\n\n### `longitude`\nLongitude of the selected location.\nRequired.\nType: number.\nExample: 46.6753\n\n### `latitude`\nLatitude of the selected location.\nRequired.\nType: number.\nExample: 24.7136\n\n### `titleAr`\nOptional Arabic short label supplied by the mobile application.\nOptional.\nType: string.\nExample: الموقع الرئيسي\n\n### `titleEn`\nOptional English short label supplied by the mobile application.\nOptional.\nType: string.\nExample: Main location\n\n### `descriptionAr`\nOptional Arabic human-readable location details supplied by the mobile application.\nOptional.\nType: string.\nExample: الرياض، المملكة العربية السعودية\n\n### `descriptionEn`\nOptional English human-readable location details supplied by the mobile application.\nOptional.\nType: string.\nExample: Riyadh, Saudi Arabia\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Location saved; the response varies according to account type and provider approval state.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `name`, `avatar`, `countryCode`, `phone`, `fullPhone`, `email`, `userType`, `status`, `statusText`, `notifyCount`, `isNotify`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "countryCode",
                  "type": "text",
                  "description": "Required with `phone` only when no bearer token is selected (pending-provider flow).\nOptional.\nType: string.\nExample variable: {{clientCountryCode}}",
                  "disabled": false,
                  "value": "{{clientCountryCode}}"
                },
                {
                  "key": "phone",
                  "type": "text",
                  "description": "Required with `countryCode` only when no bearer token is selected (pending-provider flow).\nOptional.\nType: string.\nExample variable: {{clientPhone}}",
                  "disabled": false,
                  "value": "{{clientPhone}}"
                },
                {
                  "key": "longitude",
                  "type": "text",
                  "description": "Longitude of the selected location.\nRequired.\nType: number.\nExample: 46.6753",
                  "disabled": false,
                  "value": "46.6753"
                },
                {
                  "key": "latitude",
                  "type": "text",
                  "description": "Latitude of the selected location.\nRequired.\nType: number.\nExample: 24.7136",
                  "disabled": false,
                  "value": "24.7136"
                },
                {
                  "key": "titleAr",
                  "type": "text",
                  "description": "Optional Arabic short label supplied by the mobile application.\nOptional.\nType: string.\nExample: الموقع الرئيسي",
                  "disabled": false,
                  "value": "الموقع الرئيسي"
                },
                {
                  "key": "titleEn",
                  "type": "text",
                  "description": "Optional English short label supplied by the mobile application.\nOptional.\nType: string.\nExample: Main location",
                  "disabled": false,
                  "value": "Main location"
                },
                {
                  "key": "descriptionAr",
                  "type": "text",
                  "description": "Optional Arabic human-readable location details supplied by the mobile application.\nOptional.\nType: string.\nExample: الرياض، المملكة العربية السعودية",
                  "disabled": false,
                  "value": "الرياض، المملكة العربية السعودية"
                },
                {
                  "key": "descriptionEn",
                  "type": "text",
                  "description": "Optional English human-readable location details supplied by the mobile application.\nOptional.\nType: string.\nExample: Riyadh, Saudi Arabia",
                  "disabled": false,
                  "value": "Riyadh, Saudi Arabia"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Send OTP code",
          "id": "763440bb763440bb763440bb",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/send-code",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "send-code"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSend OTP code\n\nSend an OTP code to the user's phone number.\nThis endpoint is used for both account activation and forgot-password flow.\n\n- `purpose: activation` starts or resends the activation-code flow. The current\n  validator also accepts this purpose for an already active account.\n- `purpose: forgot_password` starts password recovery.\n- AccountIdentity-backed Client/Provider roles share one recovery state and one\n  canonical password. Legacy profiles remain on the legacy recovery state.\n- The OTP expires after 1 minute and is never returned.\n- Repeated requests are throttled for 30 seconds in the running process.\n\n**Account password policy:** AccountIdentity-backed roles share one recovery state and one canonical password; legacy profiles retain their existing recovery state.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `phone`\nValid phone number for countryCode; normalized before lookup.\nRequired.\nType: string.\nExample variable: {{clientPhone}}\n\n### `countryCode`\nValid international calling code.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}\n\n### `userType`\nUser type; selects the exact identity model:\n- `client`: mobile client/customer.\n- `provider`: service provider/store owner.\nRequired.\nType: string.\nAllowed values: client, provider.\nExample: client\n\n### `purpose`\nOTP purpose:\n- `activation`: إرسال كود لتفعيل حساب غير مفعّل.\n- `forgot_password`: إرسال كود لاستعادة كلمة المرور.\nRequired.\nType: string.\nAllowed values: activation, forgot_password.\nExample: forgot_password\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- SMS provider accepted the OTP request. No OTP is returned.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `needActive`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "phone",
                  "type": "text",
                  "description": "Valid phone number for countryCode; normalized before lookup.\nRequired.\nType: string.\nExample variable: {{clientPhone}}",
                  "disabled": false,
                  "value": "{{clientPhone}}"
                },
                {
                  "key": "countryCode",
                  "type": "text",
                  "description": "Valid international calling code.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}",
                  "disabled": false,
                  "value": "{{clientCountryCode}}"
                },
                {
                  "key": "userType",
                  "type": "text",
                  "description": "User type; selects the exact identity model:\n- `client`: mobile client/customer.\n- `provider`: service provider/store owner.\nRequired.\nType: string.\nAllowed values: client, provider.\nExample: client",
                  "disabled": false,
                  "value": "client"
                },
                {
                  "key": "purpose",
                  "type": "text",
                  "description": "OTP purpose:\n- `activation`: إرسال كود لتفعيل حساب غير مفعّل.\n- `forgot_password`: إرسال كود لاستعادة كلمة المرور.\nRequired.\nType: string.\nAllowed values: activation, forgot_password.\nExample: forgot_password",
                  "disabled": false,
                  "value": "forgot_password"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Reset password after OTP verification",
          "id": "552506ff552506ff552506ff",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/change-password",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "change-password"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nReset password after OTP verification\n\n- Completes password recovery after `PATCH /activate` verifies the recovery code.\n- Requires the one-time `resetToken` field issued by that verification step.\n- Enforces the 8-128 character password policy and matching confirmation.\n- AccountIdentity-backed Client/Provider roles update only the canonical\n  `AccountIdentity.password`; legacy profile password mirrors are not authoritative.\n- Legacy profiles without AccountIdentity retain the existing User-model behavior.\n- The reset token/OTP state is invalidated, `tokenVersion` and\n  `passwordChangedAt` advance internally, and all Client/Provider UserToken\n  sessions linked to the AccountIdentity are revoked.\n- No password, OTP, or reset token is returned.\n\n**Account password policy:** Resetting an AccountIdentity-backed password updates the shared AccountIdentity credential, consumes recovery state, and revokes every linked role session.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `phone`\nValid phone number for countryCode; normalized before lookup.\nRequired.\nType: string.\nExample variable: {{clientPhone}}\n\n### `countryCode`\nValid international calling code.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}\n\n### `userType`\nUser type; selects the exact identity model:\n- `client`: mobile client/customer.\n- `provider`: service provider/store owner.\nRequired.\nType: string.\nAllowed values: client, provider.\nExample: client\n\n### `resetToken`\nOpaque, short-lived, one-time token returned by forgot_password OTP verification.\nRequired.\nType: string.\nExample: <one-time-reset-token>\n\n### `password`\nAccount password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.\n\n### `confirmPassword`\nMust match password.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Password changed; reset state consumed and previous sessions revoked.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "phone",
                  "type": "text",
                  "description": "Valid phone number for countryCode; normalized before lookup.\nRequired.\nType: string.\nExample variable: {{clientPhone}}",
                  "disabled": false,
                  "value": "{{clientPhone}}"
                },
                {
                  "key": "countryCode",
                  "type": "text",
                  "description": "Valid international calling code.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}",
                  "disabled": false,
                  "value": "{{clientCountryCode}}"
                },
                {
                  "key": "userType",
                  "type": "text",
                  "description": "User type; selects the exact identity model:\n- `client`: mobile client/customer.\n- `provider`: service provider/store owner.\nRequired.\nType: string.\nAllowed values: client, provider.\nExample: client",
                  "disabled": false,
                  "value": "client"
                },
                {
                  "key": "resetToken",
                  "type": "text",
                  "description": "Opaque, short-lived, one-time token returned by forgot_password OTP verification.\nRequired.\nType: string.\nExample: <one-time-reset-token>",
                  "disabled": false,
                  "value": "<one-time-reset-token>"
                },
                {
                  "key": "password",
                  "type": "text",
                  "description": "Account password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{clientPassword}}"
                },
                {
                  "key": "confirmPassword",
                  "type": "text",
                  "description": "Must match password.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{clientPassword}}"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Sign out",
          "id": "6e60dce26e60dce26e60dce2",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/signout",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "signout"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSign out\n\n- Signs out the authenticated client or provider from the current device.\n- Deletes the current bearer token from server-side token storage.\n- Deletes the device record matching `deviceId`.\n- After a successful response, Swagger UI removes only the bearer scheme used by this request from Authorize.\n- Does not return the token or any other sensitive value.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `deviceId`\nIdentifier of the device session to remove.\nRequired.\nType: string.\nExample: current-device-id\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Current token and device session were removed successfully.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "deviceId",
                  "type": "text",
                  "description": "Identifier of the device session to remove.\nRequired.\nType: string.\nExample: current-device-id",
                  "disabled": false,
                  "value": "current-device-id"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Delete the authenticated account",
          "id": "3eece67b3eece67b3eece67b",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/delete-account",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "delete-account"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nDelete the authenticated account\n\nDelete account is allowed only when the authenticated client/provider has no active financial, order, or auction obligations.\n\nThe account identity and type are taken only from the selected bearer token. `userId` and `userType` are not accepted in the request.\n\nDeletion is blocked for a client when:\n- Wallet balance is greater than zero.\n- A paid order is not completed, including accepted, in-progress, shipping, delivery, or returning stages.\n- The client participates in a current or upcoming auction.\n- The client owns a current or upcoming auction where that product flow is supported.\n- The client won and paid for an auction but receipt is not confirmed.\n\nDeletion is blocked for a provider when:\n- The provider has an unsettled wallet balance or earnings.\n- A financial transaction or settlement has the actual `pending` status.\n  Accepted or rejected financial records do not block deletion.\n- A paid order or business operation is still active.\n- The provider owns a current or upcoming auction.\n- A paid auction still has an unfinished receipt or delivery.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- The authenticated account was deleted successfully.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `deleted`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "04 — Client Profile",
      "id": "42612d7042612d7042612d70",
      "description": "KamTeswa API — 04 — Client Profile",
      "item": [
        {
          "name": "Update client profile",
          "id": "13ded55c13ded55c13ded55c",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/client/profile",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "client",
                "profile"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nUpdate client profile\n\n- Updates only the fields that are sent; every field is optional.\n- The client is identified from the bearer token, never from the body.\n- Phone, password, status, and every other restricted field cannot be changed here; unknown fields are rejected.\n- The response never contains a password or OTP.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `name`\nDisplay name for the account or contact.\nOptional.\nType: string.\nExample: Example Client\n\n### `email`\nLowercased and trimmed. Must not be used by another client account.\nOptional.\nType: string.\nExample variable: {{clientEmail}}\n\n### `avatar`\nOptional image. Allowed types: jpg, jpeg, png, webp (validated by file signature).\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Client profile updated successfully; returns the safe profile DTO.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `name`, `avatar`, `countryCode`, `phone`, `fullPhone`, `userType`, `status`, `active`, `token`, `tokenType`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "name",
                  "type": "text",
                  "description": "Display name for the account or contact.\nOptional.\nType: string.\nExample: Example Client",
                  "disabled": false,
                  "value": "Example Client"
                },
                {
                  "key": "email",
                  "type": "text",
                  "description": "Lowercased and trimmed. Must not be used by another client account.\nOptional.\nType: string.\nExample variable: {{clientEmail}}",
                  "disabled": false,
                  "value": "{{clientEmail}}"
                },
                {
                  "key": "avatar",
                  "type": "file",
                  "description": "Optional image. Allowed types: jpg, jpeg, png, webp (validated by file signature).\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "05 — Provider Profile",
      "id": "42c9b0f542c9b0f542c9b0f5",
      "description": "KamTeswa API — 05 — Provider Profile",
      "item": [
        {
          "name": "Verify password before changing phone",
          "id": "5d9fa8a05d9fa8a05d9fa8a0",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/change-phone/verify-password",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "change-phone",
                "verify-password"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nVerify password before changing phone\n\nVerifies the authenticated client/provider password and returns a one-purpose,\n10-minute `changePhoneToken`. AccountIdentity-backed roles verify the one shared\ncanonical AccountIdentity password; legacy profiles keep legacy verification.\nNo password is returned or logged.\n\n**Account password policy:** AccountIdentity-backed roles verify the shared canonical AccountIdentity password; legacy profiles keep legacy verification.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `password`\nAccount password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Password verified; short-lived change-phone session created.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `changePhoneToken`, `tokenType`, `userType`, `expiresIn`, `purpose`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "password",
                  "type": "text",
                  "description": "Account password. Never returned by the API.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{clientPassword}}"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Send OTP to a new phone",
          "id": "4be4f7384be4f7384be4f738",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/change-phone",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "change-phone"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSend OTP to a new phone\n\nRequires the authenticated actor and the short-lived token from the password step.\nSaves the new phone as pending and sends OTP to it; the current phone is unchanged.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `countryCode`\nPhone country code including the plus sign.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}\n\n### `updatedPhone`\nUpdated Phone field.\nRequired.\nType: string.\nExample: 0500000000\n\n### `changePhoneToken`\nShort-lived token from the change-phone password step.\nRequired.\nType: string.\nExample: <one-time-change-phone-token>\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- OTP sent to the pending phone; current phone remains unchanged.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `purpose`, `countryCode`, `updatedPhone`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "countryCode",
                  "type": "text",
                  "description": "Phone country code including the plus sign.\nRequired.\nType: string.\nExample variable: {{clientCountryCode}}",
                  "disabled": false,
                  "value": "{{clientCountryCode}}"
                },
                {
                  "key": "updatedPhone",
                  "type": "text",
                  "description": "Updated Phone field.\nRequired.\nType: string.\nExample: 0500000000",
                  "disabled": false,
                  "value": "0500000000"
                },
                {
                  "key": "changePhoneToken",
                  "type": "text",
                  "description": "Short-lived token from the change-phone password step.\nRequired.\nType: string.\nExample: <one-time-change-phone-token>",
                  "disabled": false,
                  "value": "<one-time-change-phone-token>"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Change authenticated account password",
          "id": "025780080257800802578008",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/update-password",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "update-password"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nChange authenticated account password\n\nVerifies `oldPassword`, enforces the strong-password policy for `newPassword`,\nand requires matching confirmation. This is separate from forgot-password reset.\nFor AccountIdentity-backed users, this changes the single password shared by\nClient and Provider roles, increments the internal token version, records the\npassword-change time, and revokes all linked UserToken sessions. Legacy profiles\nwithout AccountIdentity keep their existing behavior.\n\n**Account password policy:** Changing an AccountIdentity-backed password affects both roles, advances internal invalidation metadata, and revokes all linked UserToken sessions.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `oldPassword`\nCurrent account password.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.\n\n### `newPassword`\nNew account password. Must satisfy the strong-password policy.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.\n\n### `confirmPassword`\nMust match password.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Password changed; all linked sessions are revoked for AccountIdentity-backed users.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "oldPassword",
                  "type": "text",
                  "description": "Current account password.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{clientPassword}}"
                },
                {
                  "key": "newPassword",
                  "type": "text",
                  "description": "New account password. Must satisfy the strong-password policy.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{clientPassword}}"
                },
                {
                  "key": "confirmPassword",
                  "type": "text",
                  "description": "Must match password.\nRequired.\nType: string.\nExample variable: {{clientPassword}}\nNever returned by the API. Do not log this value.",
                  "disabled": false,
                  "value": "{{clientPassword}}"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get current user profile",
          "id": "0b8a2fbc0b8a2fbc0b8a2fbc",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/profile",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "profile"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet current user profile\n\n- Returns the authenticated client or provider profile.\n- Uses the bearer token actor type; client and provider credentials are never mixed.\n- The response contains no password or OTP.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Current authenticated profile.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `name`, `avatar`, `countryCode`, `phone`, `fullPhone`, `userType`, `status`, `active`, `token`, `tokenType`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Update provider profile",
          "id": "6207403e6207403e6207403e",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/provider/profile",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "provider",
                "profile"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nUpdate provider profile\n\n- Updates only the fields that are sent; every field is optional.\n- The provider is identified from the bearer token, never from the body.\n- Commercial-register files: the FINAL total is limited to 3. Final = existing − removed + added.\n  A provider with 3 files may remove one and add one in the same request (final = 3 ✅),\n  but adding without removing when already at 3 is rejected (final = 4 ❌).\n- `removeCommercialRegisterImages` only accepts files that belong to the authenticated provider.\n- Phone, password, status, approvalStatus, and every other restricted field cannot be changed here; unknown fields are rejected.\n- The response never contains a password, OTP, or internal file paths.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `name`\nDisplay name for the account or contact.\nOptional.\nType: string.\nExample: Example Provider\n\n### `email`\nLowercased and trimmed. Must not be used by another provider account.\nOptional.\nType: string.\nExample variable: {{providerEmail}}\n\n### `nationalId`\nIdentity number (free-text string, trimmed).\nOptional.\nType: string.\nExample: 1000000000\n\n### `city`\nCity id (Mongo ObjectId). Must reference an active, visible city.\nOptional.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34cf\n\n### `avatar`\nOptional image. Allowed types: jpg, jpeg, png, webp (validated by file signature).\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\n### `commercialRegisterImage`\nNew commercial-register files. Allowed types: jpg, jpeg, png, pdf (validated by file signature).\nMaximum FINAL count is 3 files: existing − removed + added ≤ 3. A file can be\nremoved and replaced in the same request as long as the final count stays ≤ 3.\nOptional.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.\n\n### `removeCommercialRegisterImages`\nCommercial-register files to remove, identified by the stored file name or the\nURL returned in `commercialRegisterImages` of the profile DTO. Accepts repeated\nform fields, a JSON-string array, or the comma-separated value emitted by Swagger\nUI. Only files owned by the authenticated provider are accepted.\nOptional.\nType: array.\nExample: []\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Provider profile updated successfully; returns the safe profile DTO.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `name`, `avatar`, `countryCode`, `phone`, `fullPhone`, `userType`, `status`, `active`, `approvalStatus`, `commercialRegisterImages`, `token`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "name",
                  "type": "text",
                  "description": "Display name for the account or contact.\nOptional.\nType: string.\nExample: Example Provider",
                  "disabled": false,
                  "value": "Example Provider"
                },
                {
                  "key": "email",
                  "type": "text",
                  "description": "Lowercased and trimmed. Must not be used by another provider account.\nOptional.\nType: string.\nExample variable: {{providerEmail}}",
                  "disabled": false,
                  "value": "{{providerEmail}}"
                },
                {
                  "key": "nationalId",
                  "type": "text",
                  "description": "Identity number (free-text string, trimmed).\nOptional.\nType: string.\nExample: 1000000000",
                  "disabled": false,
                  "value": "1000000000"
                },
                {
                  "key": "city",
                  "type": "text",
                  "description": "City id (Mongo ObjectId). Must reference an active, visible city.\nOptional.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34cf",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34cf"
                },
                {
                  "key": "avatar",
                  "type": "file",
                  "description": "Optional image. Allowed types: jpg, jpeg, png, webp (validated by file signature).\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "commercialRegisterImage",
                  "type": "file",
                  "description": "New commercial-register files. Allowed types: jpg, jpeg, png, pdf (validated by file signature).\nMaximum FINAL count is 3 files: existing − removed + added ≤ 3. A file can be\nremoved and replaced in the same request as long as the final count stays ≤ 3.\nOptional.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "removeCommercialRegisterImages",
                  "type": "text",
                  "description": "Commercial-register files to remove, identified by the stored file name or the\nURL returned in `commercialRegisterImages` of the profile DTO. Accepts repeated\nform fields, a JSON-string array, or the comma-separated value emitted by Swagger\nUI. Only files owned by the authenticated provider are accepted.\nOptional.\nType: array.\nExample: []",
                  "disabled": false,
                  "value": "[]"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List my ratings (provider)",
          "id": "3fdd7f123fdd7f123fdd7f12",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/rates?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "rates"
              ],
              "variable": [],
              "query": [
                {
                  "key": "type",
                  "value": "user",
                  "description": "Filter type as documented by the endpoint.\nOptional.\nType: string.\nAllowed values: user, product.\nExample: user",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList my ratings (provider)\n\n- Requires an authenticated **provider** bearer token (`ProviderBearerAuth`).\n- Query `type`:\n  - `user` (default when omitted) — uses `providerRate` + `providerComment` (falls back to legacy `comment`).\n  - `product` — uses `productRate` + `productComment` (falls back to legacy `comment`).\n- Each card matches Figma: `name` (the client who rated), `date`, `rate`, and `comment`.\n- `product` and `provider` objects are not returned.\n- Supports `page` and `limit`; the response carries the standard `paginate` block.\n- Clients may still call this route for their own given ratings; `type` is ignored for clients and the full `ratings` DTO is returned.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **type** (optional): Rating list type for providers. Defaults to `user` when omitted.\n  - Example: `user`\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated ratings for the authenticated provider (or client's given ratings).\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "06 — Home",
      "id": "0cc860d90cc860d90cc860d9",
      "description": "KamTeswa API — 06 — Home",
      "item": [
        {
          "name": "Get the Client Home screen as a guest or client",
          "id": "115879be115879be115879be",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Optional Bearer token. Disabled by default for guest access (SecretKeyAuth alone). Enable and use {{clientToken}} or {{providerToken}} when calling as an authenticated actor.",
                "disabled": true
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/client/home",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "client",
                "home"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet the Client Home screen as a guest or client\n\nReturns the Client Home payload used by the mobile and web designs:\n\n- client greeting and current location when a valid client token is supplied\n- notification, favorite, and business-mode quick status\n- active sliders and active main categories\n- up to 10 newest active premium products as featured ads\n\nThe bearer token is optional. Guests receive empty user/location values,\nzero personal counters, and `isFavorite: false`. A valid client token\npersonalizes those fields. Authenticated provider tokens are rejected.\nProduct images are reduced to the first image, and raw database documents\nor sensitive user fields are never returned.\n\n## Actor\nVisitor / Client\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** with SecretKeyAuth for guests.\n- Optional **ClientBearerAuth**: `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: optional Bearer token when calling as an authenticated actor\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Client Home loaded successfully.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `user`, `accountMode`, `quickStatus`, `sliders`, `categories`, `featuredAds`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get provider home screen",
          "id": "0f210b680f210b680f210b68",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/provider/home",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "provider",
                "home"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet provider home screen\n\nReturns the authenticated provider home payload used by mobile and web:\n\n- provider greeting data with `currentLocation` from the saved provider location\n- quick status (notifications, subscription, order reception, individual mode)\n- cancelled / completed order statistics for the current provider only\n- latest (`new`) orders preview (fixed top 5, no pagination)\n\nClient bearer tokens are rejected. Sliders are intentionally omitted from this\ncontract.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Provider home payload.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `provider`, `accountMode`, `quickStatus`, `statistics`, `latestOrders`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Toggle provider order reception",
          "id": "4bdf52284bdf52284bdf5228",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/isAvailable",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "isAvailable"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nToggle provider order reception\n\nToggles the authenticated provider's ability to receive new orders.\n\n- `true` means the provider is available to receive orders.\n- `false` means the provider is not available to receive orders.\n- The provider is resolved from the bearer token; no provider id or state is accepted.\n- Client bearer tokens are rejected and cannot change provider availability.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- The provider order-reception state was toggled and saved.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `isAvailable`, `isAvailableText`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "07 — Categories & Departments",
      "id": "040242640402426404024264",
      "description": "KamTeswa API — 07 — Categories & Departments",
      "item": [
        {
          "name": "List countries",
          "id": "3acfa68f3acfa68f3acfa68f",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/countries?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "countries"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList countries\n\nPaginated catalogue of active, visible countries created by the administration.\nCities for these countries are listed via `GET /cities`.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated countries.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List cities",
          "id": "2b05b54e2b05b54e2b05b54e",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/cities?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "cities"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList cities\n\nResponse list of active, visible cities (`City` model) listed right under countries.\nCreated by the administration; use after `GET /countries`.\nRoute aliases: `GET /countries/cities` and `GET /regions` remain available for backward compatibility.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Cities response list.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List departments",
          "id": "3d96a87c3d96a87c3d96a87c",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/departments?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "departments"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList departments\n\nPaginated catalogue of active main departments (`Department` collection) managed from the admin dashboard.\n\n- Public (`SecretKeyAuth` only) — registered in `MoreRoute.unRequireAuthRoutes()`.\n- Filters: `active: true`, `isDeleted: false`.\n- DTO: `@returnObj.departmentItem` → `id`, `name`.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated departments.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List sub-departments",
          "id": "476cd9da476cd9da476cd9da",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/subdepartments?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "subdepartments"
              ],
              "variable": [],
              "query": [
                {
                  "key": "departmentId",
                  "value": "665f1c2a9b4e1d0012ab34e1",
                  "description": "MongoDB ObjectId of the selected active department.\nOptional.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34e1",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList sub-departments\n\nPaginated catalogue of active sub-departments (`SubDepartment` collection) managed from the admin dashboard.\n\n- Public (`SecretKeyAuth` only) — registered in `MoreRoute.unRequireAuthRoutes()`.\n- Optional query `departmentId` filters by parent department.\n- Filters: `active: true`, `isDeleted: false`.\n- DTO: `@returnObj.subdepartmentItem` → `id`, `name`, `image`.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **departmentId** (optional): Optional parent Department Mongo id\n  - Example: `665f1c2a9b4e1d0012ab34e1`\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated sub-departments.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List departments or sub-departments (combined lookup)",
          "id": "5498174f5498174f5498174f",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/departments-list?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "departments-list"
              ],
              "variable": [],
              "query": [
                {
                  "key": "departmentId",
                  "value": "665f1c2a9b4e1d0012ab34e1",
                  "description": "MongoDB ObjectId of the selected active department.\nOptional.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34e1",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList departments or sub-departments (combined lookup)\n\nCombined lookup over the `Department` / `SubDepartment` collections.\n\n- Public (`SecretKeyAuth` only) — `MoreRoute.unRequireAuthRoutes()`.\n- Without `departmentId` → paginated main departments (`departmentItem`).\n- With `departmentId` → paginated sub-departments for that parent (`subdepartmentItem`).\n- Prefer dedicated `GET /departments` and `GET /subdepartments` when the client knows which list it needs; this endpoint keeps the older combined contract.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **departmentId** (optional): When set, returns sub-departments of this parent department instead of main departments.\n  - Example: `665f1c2a9b4e1d0012ab34e1`\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated departments (no `departmentId`) or paginated sub-departments (with `departmentId`).\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List support reasons",
          "id": "4753fd4a4753fd4a4753fd4a",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/reasons?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "reasons"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList support reasons\n\nResponse list of available support reasons created by the administration\n(`id` + localized `name` only), with standard `paginate` metadata.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Support reasons response list.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List payment methods",
          "id": "286b9bf2286b9bf2286b9bf2",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/payments?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "payments"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList payment methods\n\nAuthenticated payment-method catalogue selected by the bearer actor.\n\n- **Client token:** returns all available dashboard payment methods.\n- **Provider token:** returns the `online` payment method only; wallet and cash\n  are never included for a Provider actor.\n- Swagger UI provides a Client/Provider token switch and sends only the\n  selected authorized token.\n- The response item remains (`id`, localized `name`, `slug`, `image`).\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated payment methods.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "08 — Products",
      "id": "51c404e451c404e451c404e4",
      "description": "KamTeswa API — 08 — Products",
      "item": [
        {
          "name": "List provider non-premium accepted products",
          "id": "568a1b1b568a1b1b568a1b1b",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/provider/products/non-premium",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "provider",
                "products",
                "non-premium"
              ],
              "variable": [],
              "query": [
                {
                  "key": "keyword",
                  "value": "محراث",
                  "description": "Keyword field.\nOptional.\nType: string.\nExample: محراث",
                  "disabled": true
                }
              ]
            },
            "description": "## Purpose\nList provider non-premium accepted products\n\nReturns the authenticated provider's products that are accepted and not premium.\n\n- Provider bearer token only.\n- Client tokens are rejected.\n- Filter: `approvalStatus = accept` and `isPremium = false` for `req.user._id`.\n- Optional query `keyword`: case-insensitive partial match on product `name`.\n- Each item returns only `id` and `name`.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **keyword** (optional): Optional search term; filters products by name (case-insensitive partial match).\n  - Example: `محراث`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Non-premium accepted products for the current provider.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Add product (create)",
          "id": "0f4b9f130f4b9f130f4b9f13",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/products",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "products"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nAdd product (create)\n\n## Purpose\n**Add Product** — `POST /api/products` only.\nThis is create, not edit. Editing uses a separate endpoint:\n`PATCH /api/products?productId=`.\n\nCreate a product for the provider marketplace catalog (simple or variant).\nUsed by Mobile and Web product-builder screens after taxonomy, pricing,\nattributes (when variant), and media are collected.\n\n## Auth\nRequires **SecretKeyAuth** and **ProviderBearerAuth**.\nOwnership comes from the JWT only. Client-supplied `provider` / `providerId`\nand other workflow fields are rejected.\n\nHeaders:\n- `secretkey` — required (not `x-secret-key`)\n- `Authorization: Bearer `\n- `lang` — `ar` (default) or `en`\n\n## Transport\n`multipart/form-data` only.\n- Images (and optional video) are sent as **files**.\n- Optional video must be MP4/MOV and no larger than **20MB**.\n  Mobile/Web clients must validate or compress it before starting upload.\n- Complex fields (`name`, `description`, `attributes`, `variants`) are sent\n  as **JSON-encoded text** parts (Swagger field type = Text).\n- Do not replace this body with raw `application/json`.\n- Do not use nested multipart keys such as\n  `variants[0][attributes][0][attributeId]`.\n- Required parts: `type`, `departmentId`, `subdepartmentId`, `name`,\n  `description`, `condition`, `pricingMethod`, `images` (1–10).\n- Taxonomy IDs must be active, non-deleted, and parent-child consistent.\n- When `pricingMethod=ai`, `aiPricingRequestId` is required\n  (`POST /pricing-request` → `data.id`).\n\n## Product type rules\n`type` enum: `simple` | `variant`.\n\n1. **simple** — normal product without selectable variants.\n   Use product-level `price` / `quantity` / discount.\n   For simple products, omit `attributes` and `variants` entirely\n   (empty leftovers are rejected).\n\n2. **variant** — product with selectable combinations (Size only, Color only,\n   Size+Color, or any selected attributes supported by the catalogue).\n   `attributes` and `variants` are required JSON strings.\n   Omit top-level `price` / `quantity` / `discountType` / `discountValue`\n   (each variant carries its own).\n\n| Field | `type=simple` | `type=variant` |\n|-------|---------------|----------------|\n| `price` / `quantity` | required | **omit** |\n| `discountType` / `discountValue` | optional (`none` \\| `percentage` \\| `fixed`) | **omit** |\n| `attributes` | **omit** | required JSON string (Attribute MongoId array) |\n| `variants` | **omit** | required JSON string (variant objects) |\n\n## Attributes and variants mapping\n### Correct attributeId / valueId usage\nAttribute values live in `AttributeValue` documents:\n\n```json\n{\n  \"_id\": \"VALUE_ID\",\n  \"attribute\": \"ATTRIBUTE_ID\",\n  \"kind\": \"size\"\n}\n```\n\nIn each variant pair send:\n- `attributeId` = parent Attribute `_id` (e.g. الحجم)\n- `valueId` = AttributeValue `_id` (e.g. XL)\n\nDo **not** use a value `_id` as `attributeId`.\nDo **not** attach a Color value under the Size attribute (or the reverse).\n\n## Simple product payload\nUse `type=simple` when the product has one price and one stock quantity\nwithout selectable variants.\n\nRules:\n- Do not send `attributes`.\n- Do not send `variants`.\n- Do not send empty strings for forbidden fields.\n- Do not check “Send empty value” in Swagger/Postman — empty leftovers are rejected.\n- Use product-level `price` / `quantity` / optional discount fields.\n\nExample form-data:\n- `type` = `simple`\n- `name` = `{\"ar\":\"سماعة بلوتوث\",\"en\":\"Bluetooth Headphones\"}`\n- `description` = `{\"ar\":\"سماعات بحالة ممتازة\",\"en\":\"Headphones in excellent condition\"}`\n- `departmentId` / `subdepartmentId` = active taxonomy MongoIds\n- `condition` = `new` \\| `used`\n- `pricingMethod` = `manual`\n- `price` = `120`\n- `quantity` = `10`\n- `discountType` = `none`\n- `discountValue` = `0`\n- `images` = 1–10 files\n\nImportant: **No `attributes`. No `variants`.**\n\n## Variant product payload\nUse `type=variant` when the product has selectable options such as الحجم,\nاللون, or any attribute that changes stock/price.\n\nRules:\n- `attributes` is required (multipart **Text** JSON string).\n- `variants` is required (multipart **Text** JSON string).\n- Each variant is **one** purchasable combination.\n- Each variant must include exactly one value for every selected top-level\n  attribute.\n- **Wrong:** Size=S and Size=M inside the **same** variant.\n- **Right:** S as variant #1, M as variant #2.\n\n### `attributes` (text / JSON string)\nSelected Attribute MongoIds, e.g. الحجم + اللون:\n\n```json\n[\"6a69c8eadef712b95ba86e82\",\"6a69c8eadef712b95ba86e88\"]\n```\n\n### `variants` (text / JSON string)\nEach object must include `attributes` (`attributeId` + `valueId`),\n`quantity`, `price`, `discountType`, `discountValue`.\n\n### Mobile flow\n1. User chooses product type.\n2. If simple → send normal product fields only (omit attributes/variants).\n3. If variant → user selects attributes (e.g. الحجم, اللون).\n4. App sends selected attribute IDs in `attributes`.\n5. App builds purchasable combinations in `variants`\n   (e.g. XL + أحمر, XL + أسود).\n6. Every combination has values, price, stock, and discount data.\n\n## Examples\n**A — Simple:** Swagger example `simpleWithPercentage` (omit attributes/variants).\n\n**B — Size only (S and M are separate variants):** example `variantOneAttribute`.\n\n```json\n{\n  \"attributes\": [\"6a69c8eadef712b95ba86e82\"],\n  \"variants\": [\n    {\n      \"attributes\": [\n        {\"attributeId\":\"6a69c8eadef712b95ba86e82\",\"valueId\":\"6a69c8ebdef712b95ba86e90\"}\n      ],\n      \"quantity\": 5,\n      \"price\": 100,\n      \"discountType\": \"none\",\n      \"discountValue\": 0\n    },\n    {\n      \"attributes\": [\n        {\"attributeId\":\"6a69c8eadef712b95ba86e82\",\"valueId\":\"6a69c8ebdef712b95ba86e94\"}\n      ],\n      \"quantity\": 8,\n      \"price\": 110,\n      \"discountType\": \"none\",\n      \"discountValue\": 0\n    }\n  ]\n}\n```\n\n**C — Size + Color:** example `variantSizeColor`\n(XL+Red, XL+Black as separate rows).\n\nSeeded sample IDs (from `npm run seed:product-attributes`):\n- Size attribute `6a69c8eadef712b95ba86e82` · S `6a69c8ebdef712b95ba86e90` · M `6a69c8ebdef712b95ba86e94` · XL `6a69c8ecdef712b95ba86e9c`\n- Color attribute `6a69c8eadef712b95ba86e88` · Red `6a69c8eddef712b95ba86eb0` · Black `6a69c8eddef712b95ba86ea8`\n\n## Invalid examples (will fail validation)\n1. Same attribute twice in one variant (S + M together) — forbidden.\n2. Top-level `[Size, Color]` but variant only has Size — missing attribute.\n3. Top-level `[Size]` but variant includes Color — extra attribute.\n4. `valueId` that does not belong to `attributeId` — invalid relation.\n\n## Common mistakes\n- Sending `attributes` or `variants` for `type=simple`.\n- Sending empty value for `attributes` or `variants` (including on `type=simple`).\n- Sending invalid JSON.\n- Sending nested multipart keys like `variants[0][attributes][0][attributeId]`.\n- Sending `valueId` without its related `attributeId`.\n- Sending an `attributeId` in variants that is not in top-level `attributes`.\n- Putting two values for the same attribute inside one variant (e.g. Size S and Size M together).\n- Using unsupported `discountType` (allowed: `none`, `percentage`, `fixed`).\n- Sending non-numeric quantity / price / discountValue.\n\n## Response envelope\nCreate returns a **message-only** success envelope (no `data` field):\n\n```json\n{ \"key\": \"success\", \"message\": \"تم إنشاء المنتج بنجاح\", \"status\": 200 }\n```\n\nClients must branch on `key`, not only HTTP status.\nNewly created products return `moderationStatus: wait` (pending administration review)\nand emit the `product_submitted_for_review` administration-review event.\nThe administration review notification is created only after Product\npersistence succeeds. There is no transaction/outbox for that side-effect:\na request that fails validation, media/taxonomy/AI checks, or Product\npersistence never creates a Product-review notification.\nThe Notification record and administration counter are persisted before\nsuccess. External push delivery is deferred and is not part of the HTTP\nresponse latency or success guarantee.\n\n## Error notes\nPossible causes of product payload errors:\n- `products.invalidProductPayload` — simple/variant field mix, or invalid matrix\n- invalid JSON in `attributes` or `variants`\n- forbidden `attributes`/`variants` for simple, or missing for variant\n- invalid MongoId / taxonomy / attribute–value relation\n- unsupported `discountType` or invalid discount math\n- missing/invalid images (MIME, size, count) when required\n- invalid / non-owned `aiPricingRequestId`\n- protected ownership/workflow fields in the body\n- missing/invalid secret key or non-provider token (`unauthorized`)\n\n## Compatibility notes\n- Server-controlled: provider, workflow, moderation, deletion fields.\n- API values `variant` and `percentage`; legacy `multi_attribute` / `ratio`\n  remain readable and are normalized in responses.\n- Media uses direct multipart upload; **no media ownership registry**.\n  New files are stored under `products/{productId}/`, while MongoDB stores only each final filename (basename).\n  Previously stored full product-scoped references remain readable, and\n  provider-scoped filenames remain readable without migration.\n- `aiSuggestedPrice` is server-copied; client AI prices are rejected.\n  PricingRequest currently has no expiry or product-fingerprint fields, so\n  those integrity checks are not enforceable. The upstream AI engine remains a stub.\n- Full field tables: `docs/PRODUCTS_API_CONTRACT.md`.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `type`\nProduct shape selector.\n- `simple`: one price and one stock quantity; omit `attributes` and `variants`.\n- `variant`: selectable options; send `attributes` and `variants` as JSON strings.\nRequired.\nType: string.\nAllowed values: simple, variant.\nExample: simple\n\n### `departmentId`\nMust identify an active, non-deleted department.\nRequired.\nType: string.\nExample: 6a65c62109d85c1dbe0e86e2\n\n### `subdepartmentId`\nMust identify an active, non-deleted subdepartment that belongs to departmentId.\nRequired.\nType: string.\nExample: 6a65c81b09d85c1dbe0e8732\n\n### `name`\nJSON-encoded form-data text field with exactly ar and en (2–200 characters each).\nRequired.\nType: string.\nExample: {\"ar\":\"سماعة بلوتوث\",\"en\":\"Bluetooth Headphones\"}\n\n### `description`\nJSON-encoded form-data text field with exactly ar and en (1–500 characters each).\nRequired.\nType: string.\nExample: {\"ar\":\"سماعات بحالة ممتازة\",\"en\":\"Headphones in excellent condition\"}\n\n### `condition`\nCondition field.\nRequired.\nType: string.\nAllowed values: new, used.\nExample: used\n\n### `pricingMethod`\nSource of the selling price the client sends. Always required.\n- `manual`: client-chosen price; `aiPricingRequestId` is optional.\n- `ai`: price sourced from AI; `aiPricingRequestId` is required and\n  must be a completed provider-owned PricingRequest (`POST /pricing-request` → `data.id`).\nRequired.\nType: string.\nAllowed values: manual, ai.\nExample: manual\n\n### `aiPricingRequestId`\nRequired when `pricingMethod=ai`. First call\n[`POST /pricing-request`](#/AI%20Pricing/createPricingRequest),\nthen pass the returned `data.id` here. Optional when\n`pricingMethod=manual` (may still be sent to store the AI suggestion\nfor display). Suggested price is loaded server-side; client-submitted\nAI prices are rejected. PricingRequest currently has no expiry or\nproduct-fingerprint fields.\nOptional.\nType: string.\n\n### `quantity`\nAvailable stock quantity.\nRequired for `type=simple`.\nFor `type=variant`, omit this field — quantity belongs to each variant.\nOptional.\nType: integer.\nExample: 10\n\n### `price`\nProduct price.\nRequired for `type=simple`.\nFor `type=variant`, omit this field — price belongs to each variant.\nOptional.\nType: number.\nExample: 120\n\n### `discountType`\nDiscount strategy for a simple product.\nAllowed values: `none`, `percentage`, `fixed`.\nUse `0` for `discountValue` when `discountType=none`.\nOmit for `type=variant` (each variant has its own discount).\nOptional.\nType: string.\nAllowed values: none, percentage, fixed.\nExample: none\n\n### `discountValue`\nNumeric discount value for a simple product.\nPercentage or fixed amount depending on `discountType`.\nUse `0` when `discountType=none`. Omit for `type=variant`.\nOptional.\nType: number.\nExample: 0\n\n### `attributes`\nJSON-encoded array of Attribute MongoId strings submitted as a multipart/form-data **text** field.\n\nRequired when `type=variant`.\nForbidden when `type=simple`.\n\nDo not send this field for simple products.\nDo not send an empty value.\nDo not use “Send empty value” in Swagger or Postman.\n\nThese IDs are the selected Product Attributes for the variant product\n(for example الحجم and اللون). Every variant row must use only\n`attributeId` values included in this array.\n\nExample (Size + Color):\n```json\n[\n  \"6a69c8eadef712b95ba86e82\",\n  \"6a69c8eadef712b95ba86e88\"\n]\n```\nOptional.\nType: string.\n\n### `variants`\nJSON-encoded array of variant objects submitted as a multipart/form-data **text** field.\n\nRequired when `type=variant`.\nForbidden when `type=simple`.\n\nDo not send this field for simple products.\nDo not send an empty value.\nDo not send nested multipart keys such as\n`variants[0][attributes][0][attributeId]`.\nSend the full JSON array as the value of this single text field.\n\nEach variant is **one** purchasable combination.\nWrong: Size=S and Size=M inside the same variant.\nRight: S as variant #1, M as variant #2.\n\nID ownership:\n- `attributeId` = parent Attribute `_id`\n- `valueId` = AttributeValue `_id` that belongs to that attribute\nDo not use a value `_id` as `attributeId`.\n\nEach variant object must include exactly:\n- `attributes`: array of `{ attributeId, valueId }`\n- `quantity` (integer ≥ 1)\n- `price` (number > 0)\n- `discountType` (`none` | `percentage` | `fixed`)\n- `discountValue` (number; use `0` when `discountType=none`)\n\nBackend-enforced rules:\n- every `attributeId` inside a variant must appear in the top-level `attributes` array\n- each variant must include every selected attribute exactly once\n- each `valueId` must belong to its `attributeId`\n- duplicate variant combinations are rejected\n- quantity, price, and discountValue must be numeric\n\nExample (XL + Red):\n```json\n[\n  {\n    \"attributes\": [\n      {\n        \"attributeId\": \"6a69c8eadef712b95ba86e82\",\n        \"valueId\": \"6a69c8ecdef712b95ba86e9c\"\n      },\n      {\n        \"attributeId\": \"6a69c8eadef712b95ba86e88\",\n        \"valueId\": \"6a69c8eddef712b95ba86eb0\"\n      }\n    ],\n    \"quantity\": 5,\n    \"price\": 120,\n    \"discountType\": \"none\",\n    \"discountValue\": 0\n  }\n]\n```\nOptional.\nType: string.\n\n### `images`\nProduct images submitted as files through multipart/form-data.\nJPEG/PNG/WEBP, maximum 5MB each, 1–10 files. Direct upload; no media registry.\nRequired.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.\nExample: <binary image>\n\n### `video`\nOptional product video file (MP4/MOV, maximum 20MB). Clients must validate or compress it before starting multipart upload.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n- `attributes` and `variants` are **Text** form-data fields containing JSON strings — not nested keys.\n- For `type=simple`: omit `attributes` and `variants` (do not send empty values; do not enable “Send empty value”).\n- For `type=variant`: send both as valid JSON strings with attributeId/valueId pairs.\n- Common mistakes: nested keys like `variants[0][attributes][0][attributeId]`, invalid JSON, attributeId missing from top-level `attributes`, unsupported discountType.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Product created. Message-only response with no `data` field; clients branch on `key: success`.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "type",
                  "type": "text",
                  "description": "Product shape selector.\n- `simple`: one price and one stock quantity; omit `attributes` and `variants`.\n- `variant`: selectable options; send `attributes` and `variants` as JSON strings.\nRequired.\nType: string.\nAllowed values: simple, variant.\nExample: simple",
                  "disabled": false,
                  "value": "simple"
                },
                {
                  "key": "departmentId",
                  "type": "text",
                  "description": "Must identify an active, non-deleted department.\nRequired.\nType: string.\nExample: 6a65c62109d85c1dbe0e86e2",
                  "disabled": false,
                  "value": "6a65c62109d85c1dbe0e86e2"
                },
                {
                  "key": "subdepartmentId",
                  "type": "text",
                  "description": "Must identify an active, non-deleted subdepartment that belongs to departmentId.\nRequired.\nType: string.\nExample: 6a65c81b09d85c1dbe0e8732",
                  "disabled": false,
                  "value": "6a65c81b09d85c1dbe0e8732"
                },
                {
                  "key": "name",
                  "type": "text",
                  "description": "JSON-encoded form-data text field with exactly ar and en (2–200 characters each).\nRequired.\nType: string.\nExample: {\"ar\":\"سماعة بلوتوث\",\"en\":\"Bluetooth Headphones\"}",
                  "disabled": false,
                  "value": "{\"ar\":\"سماعة بلوتوث\",\"en\":\"Bluetooth Headphones\"}"
                },
                {
                  "key": "description",
                  "type": "text",
                  "description": "JSON-encoded form-data text field with exactly ar and en (1–500 characters each).\nRequired.\nType: string.\nExample: {\"ar\":\"سماعات بحالة ممتازة\",\"en\":\"Headphones in excellent condition\"}",
                  "disabled": false,
                  "value": "{\"ar\":\"سماعات بحالة ممتازة\",\"en\":\"Headphones in excellent condition\"}"
                },
                {
                  "key": "condition",
                  "type": "text",
                  "description": "Condition field.\nRequired.\nType: string.\nAllowed values: new, used.\nExample: used",
                  "disabled": false,
                  "value": "used"
                },
                {
                  "key": "pricingMethod",
                  "type": "text",
                  "description": "Source of the selling price the client sends. Always required.\n- `manual`: client-chosen price; `aiPricingRequestId` is optional.\n- `ai`: price sourced from AI; `aiPricingRequestId` is required and\n  must be a completed provider-owned PricingRequest (`POST /pricing-request` → `data.id`).\nRequired.\nType: string.\nAllowed values: manual, ai.\nExample: manual",
                  "disabled": false,
                  "value": "manual"
                },
                {
                  "key": "aiPricingRequestId",
                  "type": "text",
                  "description": "Required when `pricingMethod=ai`. First call\n[`POST /pricing-request`](#/AI%20Pricing/createPricingRequest),\nthen pass the returned `data.id` here. Optional when\n`pricingMethod=manual` (may still be sent to store the AI suggestion\nfor display). Suggested price is loaded server-side; client-submitted\nAI prices are rejected. PricingRequest currently has no expiry or\nproduct-fingerprint fields.\nOptional.\nType: string.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "quantity",
                  "type": "text",
                  "description": "Available stock quantity.\nRequired for `type=simple`.\nFor `type=variant`, omit this field — quantity belongs to each variant.\nOptional.\nType: integer.\nExample: 10",
                  "disabled": false,
                  "value": "10"
                },
                {
                  "key": "price",
                  "type": "text",
                  "description": "Product price.\nRequired for `type=simple`.\nFor `type=variant`, omit this field — price belongs to each variant.\nOptional.\nType: number.\nExample: 120",
                  "disabled": false,
                  "value": "120"
                },
                {
                  "key": "discountType",
                  "type": "text",
                  "description": "Discount strategy for a simple product.\nAllowed values: `none`, `percentage`, `fixed`.\nUse `0` for `discountValue` when `discountType=none`.\nOmit for `type=variant` (each variant has its own discount).\nOptional.\nType: string.\nAllowed values: none, percentage, fixed.\nExample: none",
                  "disabled": false,
                  "value": "none"
                },
                {
                  "key": "discountValue",
                  "type": "text",
                  "description": "Numeric discount value for a simple product.\nPercentage or fixed amount depending on `discountType`.\nUse `0` when `discountType=none`. Omit for `type=variant`.\nOptional.\nType: number.\nExample: 0",
                  "disabled": false,
                  "value": "0"
                },
                {
                  "key": "attributes",
                  "type": "text",
                  "description": "JSON-encoded array of Attribute MongoId strings submitted as a multipart/form-data **text** field.\n\nRequired when `type=variant`.\nForbidden when `type=simple`.\n\nDo not send this field for simple products.\nDo not send an empty value.\nDo not use “Send empty value” in Swagger or Postman.\n\nThese IDs are the selected Product Attributes for the variant product\n(for example الحجم and اللون). Every variant row must use only\n`attributeId` values included in this array.\n\nExample (Size + Color):\n```json\n[\n  \"6a69c8eadef712b95ba86e82\",\n  \"6a69c8eadef712b95ba86e88\"\n]\n```\nOptional.\nType: string.",
                  "disabled": true,
                  "value": "[\"6a69c8eadef712b95ba86e82\",\"6a69c8eadef712b95ba86e88\"]"
                },
                {
                  "key": "variants",
                  "type": "text",
                  "description": "JSON-encoded array of variant objects submitted as a multipart/form-data **text** field.\n\nRequired when `type=variant`.\nForbidden when `type=simple`.\n\nDo not send this field for simple products.\nDo not send an empty value.\nDo not send nested multipart keys such as\n`variants[0][attributes][0][attributeId]`.\nSend the full JSON array as the value of this single text field.\n\nEach variant is **one** purchasable combination.\nWrong: Size=S and Size=M inside the same variant.\nRight: S as variant #1, M as variant #2.\n\nID ownership:\n- `attributeId` = parent Attribute `_id`\n- `valueId` = AttributeValue `_id` that belongs to that attribute\nDo not use a value `_id` as `attributeId`.\n\nEach variant object must include exactly:\n- `attributes`: array of `{ attributeId, valueId }`\n- `quantity` (integer ≥ 1)\n- `price` (number > 0)\n- `discountType` (`none` | `percentage` | `fixed`)\n- `discountValue` (number; use `0` when `discountType=none`)\n\nBackend-enforced rules:\n- every `attributeId` inside a variant must appear in the top-level `attributes` array\n- each variant must include every selected attribute exactly once\n- each `valueId` must belong to its `attributeId`\n- duplicate variant combinations are rejected\n- quantity, price, and discountValue must be numeric\n\nExample (XL + Red):\n```json\n[\n  {\n    \"attributes\": [\n      {\n        \"attributeId\": \"6a69c8eadef712b95ba86e82\",\n        \"valueId\": \"6a69c8ecdef712b95ba86e9c\"\n      },\n      {\n        \"attributeId\": \"6a69c8eadef712b95ba86e88\",\n        \"valueId\": \"6a69c8eddef712b95ba86eb0\"\n      }\n    ],\n    \"quantity\": 5,\n    \"price\": 120,\n    \"discountType\": \"none\",\n    \"discountValue\": 0\n  }\n]\n```\nOptional.\nType: string.",
                  "disabled": true,
                  "value": "[{\"attributes\":[{\"attributeId\":\"6a69c8eadef712b95ba86e82\",\"valueId\":\"6a69c8ecdef712b95ba86e9c\"},{\"attributeId\":\"6a69c8eadef712b95ba86e88\",\"valueId\":\"6a69c8eddef712b95ba86eb0\"}],\"quantity\":5,\"price\":120,\"discountType\":\"none\",\"discountValue\":0}]"
                },
                {
                  "key": "images",
                  "type": "file",
                  "description": "Product images submitted as files through multipart/form-data.\nJPEG/PNG/WEBP, maximum 5MB each, 1–10 files. Direct upload; no media registry.\nRequired.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.\nExample: <binary image>",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "video",
                  "type": "file",
                  "description": "Optional product video file (MP4/MOV, maximum 20MB). Clients must validate or compress it before starting multipart upload.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List products for the authenticated client or provider",
          "id": "65d2a1d765d2a1d765d2a1d7",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/products?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "products"
              ],
              "variable": [],
              "query": [
                {
                  "key": "search",
                  "value": "",
                  "description": "Search field.\nOptional.\nType: string.",
                  "disabled": true
                },
                {
                  "key": "type",
                  "value": "",
                  "description": "Filter type as documented by the endpoint.\nOptional.\nType: string.\nAllowed values: simple, variant.",
                  "disabled": true
                },
                {
                  "key": "condition",
                  "value": "",
                  "description": "Condition field.\nOptional.\nType: string.\nAllowed values: new, used.",
                  "disabled": true
                },
                {
                  "key": "visibility",
                  "value": "",
                  "description": "Visibility field.\nOptional.\nType: boolean.",
                  "disabled": true
                },
                {
                  "key": "departmentId",
                  "value": "",
                  "description": "MongoDB ObjectId of the selected active department.\nOptional.\nType: string.",
                  "disabled": true
                },
                {
                  "key": "subdepartmentId",
                  "value": "",
                  "description": "MongoDB ObjectId of the selected active subdepartment.\nOptional.\nType: string.",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                },
                {
                  "key": "sort",
                  "value": "",
                  "description": "Sort field.\nOptional.\nType: string.\nAllowed values: createdAt, -createdAt, price, -price, quantity, -quantity, name, -name.",
                  "disabled": true
                }
              ]
            },
            "description": "## Purpose\nList products for the authenticated client or provider\n\n## Purpose\nReturns a paginated product list for an authenticated Client or Provider.\nThe response DTO and database scope are selected from the bearer role.\n\n## Actor\nClient **or** Provider.\n\n## Authorization\n- SecretKeyAuth + ProviderBearerAuth, **or**\n- SecretKeyAuth + ClientBearerAuth.\n\n## Headers\n- `secretkey`\n- `Authorization: Bearer `\n- `lang` — `ar` | `en`\n\n## Request\nQuery filters only. Pagination uses `page` + `limit` (response `paginate.perPage`).\n\n### Provider\nThe existing owner inventory behavior and `ProviderProductCard` DTO are\nunchanged. Hidden products remain visible to their owner; soft-deleted\nproducts are excluded. The `visibility` filter applies to this branch.\n\n### Client\nReturns accepted, visible, non-deleted products belonging to active providers.\n`visibility` never exposes hidden products in this branch. Each\n`ClientSharedProductCard` returns top-level `isFav` and:\n`actions.chatButton`, `actions.chatId`, `actions.detailsButton`,\n`actions.aiPricingButton`, and `actions.isFav`.\n`chatId` is empty when no direct chat exists; reading the list never creates one.\n\nTaxonomy filters `departmentId` and `subdepartmentId` are optional and can be\ncombined; both must be valid ObjectIds and are applied on top of the owner scope.\n\n## Success response\n`{ key, message, status, data, paginate }` —\n`paginate` is a **sibling** of `data` (not nested). Branch on `key`.\nBranch on bearer role for `ClientSharedProductCard[]` vs\n`ProviderProductCard[]`.\n\n`type=variant` includes both new `variant` and legacy `multi_attribute`\nrecords.\n\n## Common failures\n- Invalid filters / sort\n- Missing/invalid secret key or unsupported token role\n\n## QA notes\n- Money display uses `price` + `priceText` (no `finalPrice` / `createdAt` on list cards).\n- Visibility is `visibility` boolean derived from `isHidden`.\n- See `docs/PRODUCTS_API_CONTRACT.md`.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **search** (optional): Case-insensitive search across product name fields.\n- **type** (optional): Filter by product type. `variant` also matches legacy `multi_attribute`.\n- **condition** (optional): Product condition filter.\n- **visibility** (optional): When set, filters by owner visibility (`true` = not hidden).\n- **departmentId** (optional): Filter by main department id (from `GET /departments-list`).\n- **subdepartmentId** (optional): Filter by sub department id (from `GET /subdepartments`). Combine with `departmentId` to narrow the list.\n- **page** (optional): List page number.\n  - Example: `{{page}}`\n- **limit** (optional): Page size; mirrored as `paginate.perPage` in the list response.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n- **sort** (optional): Sort token. Prefix `-` for descending.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated product list. Branch on `key`.\n- Envelope: `{ key, message, status, data? }` — branch on `key`, not only HTTP status.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Edit product (update)",
          "id": "1a9a6c9b1a9a6c9b1a9a6c9b",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/products?productId=%7B%7BproductId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "products"
              ],
              "variable": [],
              "query": [
                {
                  "key": "productId",
                  "value": "{{productId}}",
                  "description": "MongoDB ObjectId of the product.\nRequired.\nType: string.\nExample variable: {{productId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nEdit product (update)\n\n## Purpose\n**Edit Product** — `PATCH /api/products?productId=`.\nThis is **not** Add Product. Do **not** call `POST /api/products` to edit.\n\nWhy query `productId` (not path `/api/products/{id}`)?\nThat is the real Express route in `ProductRoute.registerRoutes()` —\n`this.router.patch('/products', …)`. Swagger matches the live API.\n\nUpdate an owned provider product (simple or variant) using the same\nsimple/variant rules as Add Product. The final merged product must remain\nvalid for the effective type.\n\n## Auth\nRequires **SecretKeyAuth** and **ProviderBearerAuth**. Ownership from JWT only.\n\nHeaders: `secretkey`, `Authorization: Bearer `, `lang`.\n\n## Transport\nRequired query parameter:\n- `productId` — MongoDB ObjectId of the product to update (24-hex).\n  Required. Ownership is derived from the provider bearer token.\n\nBody: `multipart/form-data` allowlist (same fields as create; all optional).\n- Images/video are **files**.\n- `name` / `description` / `attributes` / `variants` are **JSON text** parts.\n- Do **not** use nested multipart keys such as\n  `variants[0][attributes][0][attributeId]`.\n- Do **not** check “Send empty value” for attributes/variants.\n\n## Product type rules\nSame as Add Product (`simple` | `variant`):\n\n- Effective `type=simple`: omit `attributes` and `variants`; use product-level\n  `price` / `quantity` / optional `discountType` (`none` \\| `percentage` \\| `fixed`)\n  and `discountValue`.\n- Effective `type=variant`: send `attributes` and `variants` as JSON strings;\n  omit top-level price/quantity/discount.\n- Changing type requires a complete type-specific payload so the merged\n  product stays valid.\n- When updating variants, send the **full** desired `variants` array\n  (backend replaces the matrix; there is no partial variant-row patch API).\n\n## Simple product payload\nExample form-data (with query `productId`):\n- `type` = `simple`\n- `name` = `{\"ar\":\"سماعة بلوتوث محدثة\",\"en\":\"Updated Bluetooth Headphones\"}`\n- `price` / `quantity` / `discountType=none` / `discountValue=0`\n- **Omit** `attributes` and `variants`.\n\n## Variant product payload\n`attributes` and `variants` required as multipart **Text** JSON strings when\nthe effective type is `variant`.\n\nCritical rules (same as create):\n- Each variant = **one** purchasable combination.\n- Exactly one value per selected top-level attribute.\n- Same `attributeId` must **not** repeat inside one variant.\n- `attributeId` = parent Attribute `_id`; `valueId` = AttributeValue `_id`\n  that belongs to that attribute.\n\n## Attributes and variants mapping\nSame contract as create. Seeded sample IDs:\n- Size `6a69c8eadef712b95ba86e82` · S `6a69c8ebdef712b95ba86e90` · M `6a69c8ebdef712b95ba86e94` · XL `6a69c8ecdef712b95ba86e9c`\n- Color `6a69c8eadef712b95ba86e88` · Red `6a69c8eddef712b95ba86eb0` · Black `6a69c8eddef712b95ba86ea8`\n\n### Mobile flow\n1. Choose type.\n2. Simple → normal fields only (omit attributes/variants).\n3. Variant → select attributes → send IDs in `attributes`.\n4. Build combinations → send full JSON in `variants`.\n\n## Examples\n- `simpleOmitAttributesVariants` — simple edit; attributes/variants omitted.\n- `variantSizeColor` — Size + Color (XL+Red, XL+Black) as JSON strings.\n\n## Invalid examples\nSame as create: duplicate attributeId in one variant; missing/extra attribute\nvs top-level list; `valueId` not owned by `attributeId`.\n\n## Common mistakes\n- Calling `POST /api/products` instead of this PATCH edit endpoint.\n- Missing / invalid query `productId`.\n- Sending `attributes` / `variants` for simple products.\n- Empty values for forbidden fields / “Send empty value”.\n- Nested multipart keys / invalid JSON.\n- `valueId` without matching `attributeId`, or attributeId missing from top-level list.\n- Unsupported `discountType` or non-numeric quantity/price/discountValue.\n\n## Response envelope\nHTTP 200 with `{ key, message, status, data }` where `data` is the product\ndetails DTO (not nested under `data.product`). Branch on `key`.\n\nExample:\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"تم تحديث المنتج بنجاح\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nAfter an approved-content edit, the DTO may expose `moderationStatus: wait`.\nAn effective content edit on an `accept` product returns it to `wait`.\nAfter a successful non-noop provider edit, exactly one admin notification is\ncreated with `eventType: product_updated` (deferred push). The provider also\nreceives a return-to-review notice when moderation transitions from `accept`\nto `wait`. (Previously admin edits used `approved_product_content_updated`;\nprovider edits now use the single `product_updated` admin event to avoid\nduplicates.) No admin notification is sent on validation failure or no-op.\n\n## Error notes\n- Invalid / missing / deleted / unowned product\n- Partial update would leave an invalid product state\n- Invalid attribute matrix / JSON / media / `removedImages` / `imageMode`\n- Protected fields in body\n- Unauthorized / invalid secret\n- Example fail: `{ \"key\": \"fail\", \"message\": \"حقول المنتج غير متوافقة مع نوع المنتج أو طريقة التسعير المحددة\", \"status\": 400 }`\n\n## Compatibility notes\nImages behavior (real backend):\n- Default `imageMode=append`: keep existing images; append uploaded files.\n- `removedImages` (JSON text array): delete only listed owned images.\n- `imageMode=replace` + new uploads: gallery becomes the new files only.\n- Omitting images/`removedImages`/`imageMode`: existing images unchanged.\n- New `video` replaces the previous video.\n- `removeVideo=true` clears video only (without requiring a new video file).\nPricing: `pricing.aiReference.priceRangeMin` / `priceRangeMax` /\n`suggestedPrice` come from the saved PricingRequest document when populated.\nFiles store under `products/{productId}/`; MongoDB stores filenames. See\n`docs/PRODUCTS_API_CONTRACT.md`.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `type`\nProduct shape selector for the merged result.\n- `simple`: omit `attributes` and `variants`; use product-level price/quantity.\n- `variant`: send `attributes` and `variants` as JSON strings.\nOptional.\nType: string.\nAllowed values: simple, variant.\nExample: simple\n\n### `departmentId`\nMust identify an active, non-deleted department.\nOptional.\nType: string.\n\n### `subdepartmentId`\nMust identify an active, non-deleted subdepartment that belongs to the final departmentId.\nOptional.\nType: string.\n\n### `name`\nJSON-encoded form-data text field with exactly ar and en (2–200 characters each).\nOptional.\nType: string.\nExample: {\"ar\":\"سماعة بلوتوث محدثة\",\"en\":\"Updated Bluetooth Headphones\"}\n\n### `description`\nJSON-encoded form-data text field with exactly ar and en (1–500 characters each).\nOptional.\nType: string.\n\n### `condition`\nCondition field.\nOptional.\nType: string.\nAllowed values: new, used.\n\n### `pricingMethod`\nUpdates the source of the selling price. When set to `ai`, also send\na completed provider-owned `aiPricingRequestId`.\nOptional.\nType: string.\nAllowed values: manual, ai.\n\n### `aiPricingRequestId`\nTo attach/replace the AI suggestion, first call\n[`POST /pricing-request`](#/AI%20Pricing/createPricingRequest) and\npass its returned `data.id` here. Required when `pricingMethod=ai`.\nThe request must be completed, non-deleted, and owned by the\nauthenticated provider.\nOptional.\nType: string.\n\n### `quantity`\nSimple only when the effective product type is `simple`.\nOmit when updating a `variant` product (stock lives in `variants[].quantity`).\nOptional.\nType: integer.\nExample: 9\n\n### `price`\nSimple only when the effective product type is `simple`.\nOmit for `variant` — use `variants[].price`.\nOptional.\nType: number.\nExample: 130\n\n### `discountType`\nSimple only. Enum: `none` | `percentage` | `fixed`.\nWhen `none`, send `discountValue` as `0`. Omit for variant products.\nOptional.\nType: string.\nAllowed values: none, percentage, fixed.\nExample: none\n\n### `discountValue`\nSimple only. Percentage or fixed amount depending on `discountType`.\nUse `0` with `discountType=none`. Omit for variant products.\nOptional.\nType: number.\nExample: 0\n\n### `attributes`\nJSON-encoded array of Attribute MongoId strings submitted as a multipart/form-data **text** field.\n\nRequired when the effective product `type=variant`.\nForbidden when the effective product `type=simple`.\n\nDo not send this field for simple products.\nDo not send an empty value.\nDo not use “Send empty value”.\n\nThese IDs are the selected Product Attributes (for example الحجم and اللون).\nEvery variant row must use only `attributeId` values included here.\nOptional.\nType: string.\n\n### `variants`\nJSON-encoded array of variant objects submitted as a multipart/form-data **text** field.\n\nRequired when the effective product `type=variant`.\nForbidden when the effective product `type=simple`.\n\nDo not send this field for simple products.\nDo not send an empty value.\nDo not send nested multipart keys — send one JSON string only.\n\nEach variant is one purchasable combination (`attributeId` + `valueId`),\nplus `quantity`, `price`, `discountType` (`none` | `percentage` | `fixed`),\nand `discountValue`.\nSame `attributeId` must not repeat inside one variant.\nWhen updating variants, send the full desired array (matrix replace).\nOptional.\nType: string.\n\n### `images`\nOptional new product images (JPEG/PNG/WEBP, max 5MB each, 1–10 files per request).\nDefault `imageMode=append`: keep existing images and append these files.\nWith `imageMode=replace`: these files become the full image list.\nWhen omitted (and no `removedImages`), existing images are preserved.\nOptional.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.\n\n### `removedImages`\nText field listing existing product image identifiers to remove\n(filename basename, or a product-scoped URL/path from Product Details).\n\nPreferred (Postman / mobile):\n```json\n[\"image241785318275776.png\"]\n```\n\nAlso accepted (Swagger UI multipart quirk):\n```\nimage241785318275776.png\n```\nSwagger often coerces a JSON array through `Array#toString()`, so a\nsingle filename or comma-separated filenames are accepted and\nnormalized to an array server-side.\n\nSubmitted as multipart/form-data text — not nested keys.\nDo not check “Send empty value”.\nPath traversal (`..`) and foreign product paths are rejected.\nCan be combined with new `images` uploads under `imageMode=append`.\nOptional.\nType: string.\n\n### `imageMode`\nHow uploaded images interact with the existing gallery.\n- `append` (default): keep remaining old images, apply `removedImages`, then append uploads.\n- `replace`: discard all old images and use uploaded `images` only (uploads required).\nOptional.\nType: string.\nAllowed values: append, replace.\n\n### `video`\nWhen supplied, replaces the previous product video (MP4/MOV, max 20MB).\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\n### `removeVideo`\nWhen `true`, clears the product video without requiring a new video file.\nOptional.\nType: boolean.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n- `attributes` and `variants` are **Text** form-data fields containing JSON strings — not nested keys.\n- For `type=simple`: omit `attributes` and `variants` (do not send empty values; do not enable “Send empty value”).\n- For `type=variant`: send both as valid JSON strings with attributeId/valueId pairs.\n- Common mistakes: nested keys like `variants[0][attributes][0][attributeId]`, invalid JSON, attributeId missing from top-level `attributes`, unsupported discountType.\n\n## Query Parameters\n- **productId** (required): Required MongoDB ObjectId of the product to act on (24-hex).\nUsed by Edit (`PATCH /products`), delete, details, visibility, and premium.\nOwnership is derived from the provider bearer token — do not send a foreign id.\nThis is a **query** parameter on `/api/products`, not a path `/api/products/{id}`.\n  - Example: `665f1c2a9b4e1d0012ab34d0`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Updated product details. Branch on `key: success`.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "type",
                  "type": "text",
                  "description": "Product shape selector for the merged result.\n- `simple`: omit `attributes` and `variants`; use product-level price/quantity.\n- `variant`: send `attributes` and `variants` as JSON strings.\nOptional.\nType: string.\nAllowed values: simple, variant.\nExample: simple",
                  "disabled": false,
                  "value": "simple"
                },
                {
                  "key": "departmentId",
                  "type": "text",
                  "description": "Must identify an active, non-deleted department.\nOptional.\nType: string.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "subdepartmentId",
                  "type": "text",
                  "description": "Must identify an active, non-deleted subdepartment that belongs to the final departmentId.\nOptional.\nType: string.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "name",
                  "type": "text",
                  "description": "JSON-encoded form-data text field with exactly ar and en (2–200 characters each).\nOptional.\nType: string.\nExample: {\"ar\":\"سماعة بلوتوث محدثة\",\"en\":\"Updated Bluetooth Headphones\"}",
                  "disabled": false,
                  "value": "{\"ar\":\"سماعة بلوتوث محدثة\",\"en\":\"Updated Bluetooth Headphones\"}"
                },
                {
                  "key": "description",
                  "type": "text",
                  "description": "JSON-encoded form-data text field with exactly ar and en (1–500 characters each).\nOptional.\nType: string.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "condition",
                  "type": "text",
                  "description": "Condition field.\nOptional.\nType: string.\nAllowed values: new, used.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "pricingMethod",
                  "type": "text",
                  "description": "Updates the source of the selling price. When set to `ai`, also send\na completed provider-owned `aiPricingRequestId`.\nOptional.\nType: string.\nAllowed values: manual, ai.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "aiPricingRequestId",
                  "type": "text",
                  "description": "To attach/replace the AI suggestion, first call\n[`POST /pricing-request`](#/AI%20Pricing/createPricingRequest) and\npass its returned `data.id` here. Required when `pricingMethod=ai`.\nThe request must be completed, non-deleted, and owned by the\nauthenticated provider.\nOptional.\nType: string.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "quantity",
                  "type": "text",
                  "description": "Simple only when the effective product type is `simple`.\nOmit when updating a `variant` product (stock lives in `variants[].quantity`).\nOptional.\nType: integer.\nExample: 9",
                  "disabled": false,
                  "value": "9"
                },
                {
                  "key": "price",
                  "type": "text",
                  "description": "Simple only when the effective product type is `simple`.\nOmit for `variant` — use `variants[].price`.\nOptional.\nType: number.\nExample: 130",
                  "disabled": false,
                  "value": "130"
                },
                {
                  "key": "discountType",
                  "type": "text",
                  "description": "Simple only. Enum: `none` | `percentage` | `fixed`.\nWhen `none`, send `discountValue` as `0`. Omit for variant products.\nOptional.\nType: string.\nAllowed values: none, percentage, fixed.\nExample: none",
                  "disabled": false,
                  "value": "none"
                },
                {
                  "key": "discountValue",
                  "type": "text",
                  "description": "Simple only. Percentage or fixed amount depending on `discountType`.\nUse `0` with `discountType=none`. Omit for variant products.\nOptional.\nType: number.\nExample: 0",
                  "disabled": false,
                  "value": "0"
                },
                {
                  "key": "attributes",
                  "type": "text",
                  "description": "JSON-encoded array of Attribute MongoId strings submitted as a multipart/form-data **text** field.\n\nRequired when the effective product `type=variant`.\nForbidden when the effective product `type=simple`.\n\nDo not send this field for simple products.\nDo not send an empty value.\nDo not use “Send empty value”.\n\nThese IDs are the selected Product Attributes (for example الحجم and اللون).\nEvery variant row must use only `attributeId` values included here.\nOptional.\nType: string.",
                  "disabled": true,
                  "value": "[\"6a69c8eadef712b95ba86e82\",\"6a69c8eadef712b95ba86e88\"]"
                },
                {
                  "key": "variants",
                  "type": "text",
                  "description": "JSON-encoded array of variant objects submitted as a multipart/form-data **text** field.\n\nRequired when the effective product `type=variant`.\nForbidden when the effective product `type=simple`.\n\nDo not send this field for simple products.\nDo not send an empty value.\nDo not send nested multipart keys — send one JSON string only.\n\nEach variant is one purchasable combination (`attributeId` + `valueId`),\nplus `quantity`, `price`, `discountType` (`none` | `percentage` | `fixed`),\nand `discountValue`.\nSame `attributeId` must not repeat inside one variant.\nWhen updating variants, send the full desired array (matrix replace).\nOptional.\nType: string.",
                  "disabled": true,
                  "value": "[{\"attributes\":[{\"attributeId\":\"6a69c8eadef712b95ba86e82\",\"valueId\":\"6a69c8ecdef712b95ba86e9c\"},{\"attributeId\":\"6a69c8eadef712b95ba86e88\",\"valueId\":\"6a69c8eddef712b95ba86eb0\"}],\"quantity\":5,\"price\":120,\"discountType\":\"none\",\"discountValue\":0},{\"attributes\":[{\"attributeId\":\"6a69c8eadef712b95ba86e82\",\"valueId\":\"6a69c8ecdef712b95ba86e9c\"},{\"attributeId\":\"6a69c8eadef712b95ba86e88\",\"valueId\":\"6a69c8eddef712b95ba86ea8\"}],\"quantity\":3,\"price\":125,\"discountType\":\"none\",\"discountValue\":0}]"
                },
                {
                  "key": "images",
                  "type": "file",
                  "description": "Optional new product images (JPEG/PNG/WEBP, max 5MB each, 1–10 files per request).\nDefault `imageMode=append`: keep existing images and append these files.\nWith `imageMode=replace`: these files become the full image list.\nWhen omitted (and no `removedImages`), existing images are preserved.\nOptional.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "removedImages",
                  "type": "text",
                  "description": "Text field listing existing product image identifiers to remove\n(filename basename, or a product-scoped URL/path from Product Details).\n\nPreferred (Postman / mobile):\n```json\n[\"image241785318275776.png\"]\n```\n\nAlso accepted (Swagger UI multipart quirk):\n```\nimage241785318275776.png\n```\nSwagger often coerces a JSON array through `Array#toString()`, so a\nsingle filename or comma-separated filenames are accepted and\nnormalized to an array server-side.\n\nSubmitted as multipart/form-data text — not nested keys.\nDo not check “Send empty value”.\nPath traversal (`..`) and foreign product paths are rejected.\nCan be combined with new `images` uploads under `imageMode=append`.\nOptional.\nType: string.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "imageMode",
                  "type": "text",
                  "description": "How uploaded images interact with the existing gallery.\n- `append` (default): keep remaining old images, apply `removedImages`, then append uploads.\n- `replace`: discard all old images and use uploaded `images` only (uploads required).\nOptional.\nType: string.\nAllowed values: append, replace.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "video",
                  "type": "file",
                  "description": "When supplied, replaces the previous product video (MP4/MOV, max 20MB).\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "removeVideo",
                  "type": "text",
                  "description": "When `true`, clears the product video without requiring a new video file.\nOptional.\nType: boolean.",
                  "disabled": false,
                  "value": ""
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Soft-delete an owned product",
          "id": "581c09a2581c09a2581c09a2",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/products?productId=%7B%7BproductId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "products"
              ],
              "variable": [],
              "query": [
                {
                  "key": "productId",
                  "value": "{{productId}}",
                  "description": "MongoDB ObjectId of the product.\nRequired.\nType: string.\nExample variable: {{productId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nSoft-delete an owned product\n\n## Purpose\nSoft-deletes an owned product from list or details screens. Sets deleted\nworkflow status and records `deletedAt` and provider-derived `deletedBy`.\nProduct, variants, media files, and historical references are preserved.\n\n## Actor\nProvider.\n\n## Authorization\nSecretKeyAuth + ProviderBearerAuth.\n\n## Headers\n- `secretkey`\n- `Authorization: Bearer `\n- `lang`\n\n## Request\nQuery `productId` only.\n\n## Success response\nHTTP 200, `key: success`, `data: { productId, deleted: true }`.\n\n## Common failures\n- Invalid/missing product, wrong owner, already deleted\n- An active Order still requiring payment, fulfillment, delivery, or cancellation handling\n- A scheduled/live Auction, or a finished bidding cycle with a winner that is\n  still awaiting payment or delivery\n- Unauthorized / invalid secret\n\nCompleted/cancelled Order history, cancelled Auctions, and delivered Auction\nhistory remain readable and do not block deletion. A repeated delete follows\nthe existing convention and returns the same masked not-found failure.\n\n## QA notes\n- Same endpoint for list and details delete actions.\n- Soft delete only; media is not erased from disk by this operation.\n- Does not change moderation; the existing admin deletion notification remains unchanged.\n- See `docs/PRODUCTS_API_CONTRACT.md`.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **productId** (required): Required MongoDB ObjectId of the product to act on (24-hex).\nUsed by Edit (`PATCH /products`), delete, details, visibility, and premium.\nOwnership is derived from the provider bearer token — do not send a foreign id.\nThis is a **query** parameter on `/api/products`, not a path `/api/products/{id}`.\n  - Example: `665f1c2a9b4e1d0012ab34d0`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Product soft-deleted. Branch on `key: success`.\n- Envelope: `{ key, message, status, data? }` — branch on `key`, not only HTTP status.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List all similar products with pagination",
          "id": "73f8c7fb73f8c7fb73f8c7fb",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Optional Bearer token. Disabled by default for guest access (SecretKeyAuth alone). Enable and use {{clientToken}} or {{providerToken}} when calling as an authenticated actor.",
                "disabled": true
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/products/similar?productId=%7B%7BproductId%7D%7D&page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "products",
                "similar"
              ],
              "variable": [],
              "query": [
                {
                  "key": "productId",
                  "value": "{{productId}}",
                  "description": "MongoDB ObjectId of the product.\nRequired.\nType: string.\nExample variable: {{productId}}",
                  "disabled": false
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList all similar products with pagination\n\nReturns the full paginated similar-product catalogue used after the\nProduct Details \"show more\" action.\n\nSimilarity and stable ordering:\n1. accepted, active, visible, non-deleted products in the same\n   `subDepartment`;\n2. accepted, active, visible, non-deleted products in the same\n   `department` as fallback;\n3. within each priority, featured products first, then newest first,\n   then `_id` descending as the stable tie-breaker.\n\nThe current product and products owned by blocked/deleted providers are\nexcluded. One combined aggregation applies pagination after priority\nordering, so department fallback never jumps ahead of same-subdepartment\nresults and duplicates are not introduced.\n\n`secretkey` is required. A Client bearer token is optional and only\npersonalizes `isFavorite`; authenticated Provider tokens are rejected.\nGuest items always return `isFavorite: false`.\n\n`page` defaults to `1`. `limit` defaults to `10` and cannot exceed `30`.\n`paginate.total` counts every valid similar product after filtering.\n\nIf the source product has neither department nor subDepartment, the\nendpoint returns an empty `items` array with a zero-total paginate block.\nIt never returns unrelated random products.\n\nThis endpoint is independent from Product Details. Product Details keeps\nits existing preview of at most 4 items, `hasMore`, and no paginate block.\n\nQA ordering: same subDepartment first, then department fallback. Product Details preview capped at 4.\n\n## Actor\nVisitor / Client\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** with SecretKeyAuth for guests.\n- Optional **ClientBearerAuth**: `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: optional Bearer token when calling as an authenticated actor\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **productId** (required): Active, accepted, visible product MongoDB ObjectId.\n  - Example: `665f1c2a9b4e1d0012ab34d0`\n- **page** (optional): Combined-result page number.\n  - Example: `{{page}}`\n- **limit** (optional): Items per page, maximum 30.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Similar products loaded with nested pagination metadata.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `items`, `paginate`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Report a product",
          "id": "457b0bb0457b0bb0457b0bb0",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/products/report",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "products",
                "report"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nReport a product\n\nCreates a pending report from an authenticated client.\n\nThe `multipart/form-data` body accepts **only** the required `productId`,\nrequired `reasonId`, and optional `note`. `providerId`, reporter identity,\nworkflow status, product number, and snapshot fields are forbidden.\n\nThe provider is resolved server-side from the active, non-deleted Product\ndocument. The server snapshots the product name/number and bilingual\nreason data so the review history remains readable if catalogue text\nchanges later.\n\nDuplicate policy: one pending report is allowed per\nreporter + product + reason. A new report may be submitted after the\nprevious matching report leaves `pending`.\n\nAfter a successful create, one persisted notification is routed to the\nactive super administrator and one to the product provider. Both include\nproduct name, product number, and reason. The provider notification never\nexposes reporter identity. No notification is sent for a rejected\nduplicate.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `productId`\nExisting, non-deleted Product ObjectId.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0\n\n### `reasonId`\nSupport reason identifier from lookups.\nRequired.\nType: string.\nExample: 66a1b2c3d4e5f67890123456\n\n### `note`\nOptional plain-text client context. HTML angle brackets are rejected.\nOptional.\nType: string.\nExample: السعر والصور مكررة في إعلان آخر\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Report created and notification records persisted.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `report`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "productId",
                  "type": "text",
                  "description": "Existing, non-deleted Product ObjectId.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34d0"
                },
                {
                  "key": "reasonId",
                  "type": "text",
                  "description": "Support reason identifier from lookups.\nRequired.\nType: string.\nExample: 66a1b2c3d4e5f67890123456",
                  "disabled": false,
                  "value": "66a1b2c3d4e5f67890123456"
                },
                {
                  "key": "note",
                  "type": "text",
                  "description": "Optional plain-text client context. HTML angle brackets are rejected.\nOptional.\nType: string.\nExample: السعر والصور مكررة في إعلان آخر",
                  "disabled": false,
                  "value": "السعر والصور مكررة في إعلان آخر"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get product details (client or provider)",
          "id": "6c15545c6c15545c6c15545c",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/products/details?productId=%7B%7BproductId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "products",
                "details"
              ],
              "variable": [],
              "query": [
                {
                  "key": "productId",
                  "value": "{{productId}}",
                  "description": "MongoDB ObjectId of the product.\nRequired.\nType: string.\nExample variable: {{productId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nGet product details (client or provider)\n\n## Purpose\nReturns product details for an authenticated **Client** or **Provider**.\nSwitch the bearer token the same way as other dual-token endpoints.\n\n## Actor\nClient **or** Provider (OR security schemes below).\n\n## Authorization\n- `SecretKeyAuth` + `ProviderBearerAuth`, **or**\n- `SecretKeyAuth` + `ClientBearerAuth`\n\n## Headers\n- `secretkey`\n- `Authorization: Bearer `\n- `lang` — `ar` | `en`\n\n## Request\nRequired query `productId` only.\n\n## Role behavior\n### Provider\nOwner-only admin details (`ProviderProductDetails`).\nHidden and pending products remain readable by their owner.\nSoft-deleted products and another provider's identifiers are masked with the\nsame not-found response.\n\n### Client\nPublic storefront details for an accepted, visible product.\nResponse matches the client product-details contract and includes\n`similarAds` for the same product (same subDepartment first, then\ndepartment fallback; max 4 items + `hasMore`; no nested `paginate`).\nIt also returns top-level `isFav` plus\n`actions.{chatButton,chatId,detailsButton,aiPricingButton,isFav}`.\n`chatId` is the existing direct chat id or an empty string; this read\nnever creates a chat.\nHidden, pending, rejected, deleted, or non-accepted products return the\nsame not-found mask.\n\n## Success response\n`{ key, message, status, data }`. Branch on `key` and actor role.\n\n### Variant read shape (provider details only)\n`attributes` is the **option catalogue** for this product (grouped attribute +\nvalues used by its variants only).\n\n`variants` is the list of **purchasable combinations**. Each row is compact:\n- `label` — human-readable summary (e.g. `XS / أسود`)\n- `selectedValues` — map `{ [attributeId]: valueId }`\n- `quantity` / `price` / `discountType` / `discountValue` / `isAvailable`\n\nFull attribute/value display data lives once under top-level `attributes`.\nVariants do **not** repeat nested `{ attribute, value }` objects.\n\nProduct Add (`POST /products`) and Edit (`PATCH /products`) request bodies\nare unchanged — they still send `attributes` / `variants` as JSON strings.\n\nExample (variant excerpt):\n```json\n{\n  \"attributes\": [\n    {\n      \"id\": \"6a69c8eadef712b95ba86e82\",\n      \"name\": \"الحجم\",\n      \"kind\": \"size\",\n      \"values\": [\n        { \"id\": \"6a69c8eadef712b95ba86e8c\", \"name\": \"XS\", \"kind\": \"size\", \"colorCode\": null }\n      ]\n    },\n    {\n      \"id\": \"6a69c8eadef712b95ba86e88\",\n      \"name\": \"اللون\",\n      \"kind\": \"color\",\n      \"values\": [\n        { \"id\": \"6a69c8eddef712b95ba86ea8\", \"name\": \"أسود\", \"kind\": \"color\", \"colorCode\": \"#111827\" }\n      ]\n    }\n  ],\n  \"variants\": [\n    {\n      \"id\": \"6a69c8eadef712b95ba86e82:6a69c8eadef712b95ba86e8c|6a69c8eadef712b95ba86e88:6a69c8eddef712b95ba86ea8\",\n      \"label\": \"XS / أسود\",\n      \"selectedValues\": {\n        \"6a69c8eadef712b95ba86e82\": \"6a69c8eadef712b95ba86e8c\",\n        \"6a69c8eadef712b95ba86e88\": \"6a69c8eddef712b95ba86ea8\"\n      },\n      \"quantity\": 5,\n      \"price\": 120,\n      \"discountType\": \"none\",\n      \"discountValue\": 0,\n      \"isAvailable\": true\n    }\n  ]\n}\n```\n\nSimple products return `attributes: []` and `variants: []` (no fake option data).\n\n## Common failures\n- Invalid / missing `productId`\n- Product not found for this actor (provider ownership / client visibility)\n- Missing/invalid secret key or unauthorized token role\n\n## QA notes\n- List remains `GET /products`; details are this dedicated path.\n- See `docs/PRODUCTS_API_CONTRACT.md`.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **productId** (required): Product identifier (owned for provider; public-eligible for client).\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Product details. Provider responses use `ProviderProductDetails`.\nClient responses use the client product-details DTO including `similarAds`.\nBranch on `key` and actor role.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `type`, `typeText`, `status`, `statusText`, `moderationStatus`, `moderationStatusText`, `rejectionReason`, `isVisible`, `visibilityText`, `name`, `description`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Toggle visibility for an owned accepted product",
          "id": "7ad396fa7ad396fa7ad396fa",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/products/visibility?productId=%7B%7BproductId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "products",
                "visibility"
              ],
              "variable": [],
              "query": [
                {
                  "key": "productId",
                  "value": "{{productId}}",
                  "description": "MongoDB ObjectId of the product.\nRequired.\nType: string.\nExample variable: {{productId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nToggle visibility for an owned accepted product\n\n## Purpose\nToggles owner visibility for one product (`isHidden` flipped in DB).\nRequest sends **only** query `productId` — no body and no `isVisible` field.\nValidates that the product exists, is owned by the provider, and is not\ndeleted. Does **not** require `approvalStatus = accept`, premium\nsubscription, or `isPremium` checks. The handler only flips `isHidden`.\n\nHidden products remain in provider list/details responses but are excluded\nby public product listing filters. Visibility never creates a Product-review notification.\n\n## Actor\nProvider.\n\n## Authorization\nSecretKeyAuth + ProviderBearerAuth.\n\n## Headers\n- `secretkey`\n- `Authorization: Bearer `\n- `lang`\n\n## Request\nQuery `productId` only (required Mongo ObjectId). No request body.\n\n## Preconditions (validated before the handler runs)\n- Product exists, is owned by the authenticated provider, and is not soft-deleted\n\nPremium subscription and administration approval are **not** required.\nThe handler toggles `isHidden` (`true` ↔ `false`).\n\n## Success response\nHTTP 200. Message-only envelope `{ key, message, status }` where `key` is\n`success`. The response intentionally has no `data` field. Clients must\nbranch on `key`, not only HTTP status.\n\nMessage depends on the new visibility state:\n- Product was shown → hidden: `تم إخفاء المنتج بنجاح`\n- Product was hidden → shown: `تم إظهار المنتج بنجاح`\n\n## Common failures\n- Missing / invalid `productId`\n- Product missing, deleted, or not owned by the provider\n- Unauthorized / invalid secret / non-provider token\n\n## QA notes\n- Toggle from list and details uses the same endpoint with `productId` only.\n- Unlike `PATCH /products/premium`, visibility does not require\n  `approvalStatus = accept` or an active premium subscription.\n- Does not change moderation or workflow status and does not notify review.\n- See `docs/PRODUCTS_API_CONTRACT.md`.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **productId** (required): Required MongoDB ObjectId of the product to act on (24-hex).\nUsed by Edit (`PATCH /products`), delete, details, visibility, and premium.\nOwnership is derived from the provider bearer token — do not send a foreign id.\nThis is a **query** parameter on `/api/products`, not a path `/api/products/{id}`.\n  - Example: `665f1c2a9b4e1d0012ab34d0`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Visibility toggled. Message-only response with no `data` field; clients branch on `key: success`.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "09 — Client Order",
      "id": "1eee39d01eee39d01eee39d0",
      "description": "KamTeswa API — 09 — Client Order",
      "item": [
        {
          "name": "Confirm a direct-buy order",
          "id": "05812fff05812fff05812fff",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/order",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "order"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nConfirm a direct-buy order\n\nCreates a purchase order for a single product (direct buy).\n\n- Client bearer token only; provider tokens are rejected.\n- `count` must not exceed the available stock; stock is reserved atomically\n  on creation.\n- The order starts as `new`, awaiting provider approval, with a\n  purchase-time product snapshot stored on it.\n- Creates a private order chat with `_id` equal to the order id\n  (`orderRef=Order`) between the client and store.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `productId`\nMongoDB ObjectId of the product.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34ce\n\n### `count`\nCount field.\nRequired.\nType: integer.\nExample: 2\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Order created and sent to the store for review.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "productId",
                  "type": "text",
                  "description": "MongoDB ObjectId of the product.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34ce",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34ce"
                },
                {
                  "key": "count",
                  "type": "text",
                  "description": "Count field.\nRequired.\nType: integer.\nExample: 2",
                  "disabled": false,
                  "value": "2"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Pay for an accepted order",
          "id": "18802ff618802ff618802ff6",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/order/payment",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "order",
                "payment"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nPay for an accepted order\n\nPays for an order the provider has already accepted.\n\n- Client bearer token only.\n- Payable only while the order is `new`, accepted, unpaid, and inside the\n  payment window.\n- `wallet` requires sufficient balance; `online` uses the online strategy.\n- On success the order moves to `current` / `processing`.\n- Creates a platform `Profit` record only (no provider FinancialTransaction yet).\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `orderId`\nOrder Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0\n\n### `paymentMethod`\nPayment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Payment completed.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "orderId",
                  "type": "text",
                  "description": "Order Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34d0"
                },
                {
                  "key": "paymentMethod",
                  "type": "text",
                  "description": "Payment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet",
                  "disabled": false,
                  "value": "wallet"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Cancel an order awaiting approval",
          "id": "31437c1231437c1231437c12",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/order/cancel",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "order",
                "cancel"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nCancel an order awaiting approval\n\nCancels an order that is still `new` and has not been accepted or paid.\n\n- Client bearer token only.\n- Reserved stock is restored on cancellation.\n- `reason` must be a Reason ObjectId taken from `GET /reasons`, not free text.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `orderId`\nOrder Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0\n\n### `reason`\nReason ObjectId from the shared reasons catalogue.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34aa\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Order cancelled.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "orderId",
                  "type": "text",
                  "description": "Order Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34d0"
                },
                {
                  "key": "reason",
                  "type": "text",
                  "description": "Reason ObjectId from the shared reasons catalogue.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34aa",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34aa"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Confirm receipt (complete order)",
          "id": "3a87ce873a87ce873a87ce87",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/order/received?orderId=665f1c2a9b4e1d0012ab34d0",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "order",
                "received"
              ],
              "variable": [],
              "query": [
                {
                  "key": "orderId",
                  "value": "665f1c2a9b4e1d0012ab34d0",
                  "description": "Order Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nConfirm receipt (complete order)\n\nClient confirms that they received the product/order.\n\n- Client bearer token only.\n- Allowed only when the order is paid (`isPayment=true`), `current`,\n  and `currentStep=delivered_to_customer`.\n- Provider delivery alone does **not** complete the order; this is the\n  final step that sets `status=finished`, `receivedAt`, and `completedAt`.\n- Creates the provider pending `FinancialTransaction` on successful receipt.\n- Unpaid, cancelled, already-finished, or not-yet-delivered orders are rejected.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **orderId** (required): Order Id query parameter.\n  - Example: `665f1c2a9b4e1d0012ab34d0`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Order completed after client receipt confirmation.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Rate store and product after a finished order",
          "id": "5b054a0f5b054a0f5b054a0f",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/rate",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "rate"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nRate store and product after a finished order\n\nClient submits **one** rating for a finished order that scores **both** the store\nand the product in a single request, each with its own optional comment.\n\nRequest body (multipart/form-data) — flat keys only:\n- `orderId` — finished order owned by the client\n- `productRate` — product stars (1–5)\n- `productComment` — optional product comment\n- `providerRate` — store stars (1–5)\n- `providerComment` — optional store comment\n\nBusiness rules:\n- **Client bearer only** (`ClientBearerAuth`). The caller must own the order.\n- Order must be `finished`, paid (`isPayment: true`), and received (`isReceived: true`).\n- **Once per order** — a second call is rejected.\n- Creates one `Rate` linking the client, order, product, and provider, then sets `order.rate`.\n- Stores product and provider comments separately.\n- Recalculates provider/product averages and rating counts.\n- Re-rate / edit after the first submit is not supported.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `orderId`\nFinished order id owned by the authenticated client.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34ce\n\n### `productRate`\nProduct stars (1–5).\nRequired.\nType: number.\nExample: 4\n\n### `productComment`\nOptional free-text comment for the product only.\nOptional.\nType: string.\nExample: منتج ممتاز وجودته عالية\n\n### `providerRate`\nStore/provider stars (1–5).\nRequired.\nType: number.\nExample: 5\n\n### `providerComment`\nOptional free-text comment for the store only.\nOptional.\nType: string.\nExample: تعامل سريع وخدمة احترافية\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Rating created; provider and product averages updated.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "orderId",
                  "type": "text",
                  "description": "Finished order id owned by the authenticated client.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34ce",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34ce"
                },
                {
                  "key": "productRate",
                  "type": "text",
                  "description": "Product stars (1–5).\nRequired.\nType: number.\nExample: 4",
                  "disabled": false,
                  "value": "4"
                },
                {
                  "key": "productComment",
                  "type": "text",
                  "description": "Optional free-text comment for the product only.\nOptional.\nType: string.\nExample: منتج ممتاز وجودته عالية",
                  "disabled": false,
                  "value": "منتج ممتاز وجودته عالية"
                },
                {
                  "key": "providerRate",
                  "type": "text",
                  "description": "Store/provider stars (1–5).\nRequired.\nType: number.\nExample: 5",
                  "disabled": false,
                  "value": "5"
                },
                {
                  "key": "providerComment",
                  "type": "text",
                  "description": "Optional free-text comment for the store only.\nOptional.\nType: string.\nExample: تعامل سريع وخدمة احترافية",
                  "disabled": false,
                  "value": "تعامل سريع وخدمة احترافية"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Create a return request within the allowed return window",
          "id": "07d18e9f07d18e9f07d18e9f",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/return-request",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "return-request"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nCreate a return request for a completed order\n\nClient creates a return request for an owned completed/finished and paid order.\nAll fields are supplied via **body** (`orderId`, `reason`, optional `attachments`). Prefer `multipart/form-data` when uploading attachments.\nStatus flow starts at `pending_review`.\nAttachments are optional images (JPEG/PNG/WebP), max 10 files, 5MB each.\nOne return request is allowed permanently per original order. If a previous return was rejected, create returns `returnAlreadyRejected`.\nCreation is allowed only during the dashboard-configured `returnRequestAllowedDays` window. The server counts exact 24-hour days from `Order.completedAt` (with `receivedAt`, then `updatedAt`, as legacy fallbacks) and returns `returnWindowExpired` after the deadline.\nWallet refund does **not** happen on create — only on provider accept for wallet-paid orders.\nList/details for returns use the shared endpoints:\n- `GET /orders?type=return`\n- `GET /order?type=return&id=...`\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `orderId`\nOriginal completed order ObjectId owned by the authenticated client.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0\n\n### `reason`\nReason field.\nRequired.\nType: string.\nExample: المنتج تالف\n\n### `attachments`\nOptional evidence image (JPEG/PNG/WebP, max 5MB).\nLeave empty if unused. Repeat the same field for up to 10 files.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Return request created.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "orderId",
                  "type": "text",
                  "description": "Original completed order ObjectId owned by the authenticated client.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34d0"
                },
                {
                  "key": "reason",
                  "type": "text",
                  "description": "Reason field.\nRequired.\nType: string.\nExample: المنتج تالف",
                  "disabled": false,
                  "value": "المنتج تالف"
                },
                {
                  "key": "attachments",
                  "type": "file",
                  "description": "Optional evidence image (JPEG/PNG/WebP, max 5MB).\nLeave empty if unused. Repeat the same field for up to 10 files.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Mark returned product as handed/delivered",
          "id": "306631223066312230663122",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/return-request/delivered?id=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "return-request",
                "delivered"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nMark returned product as handed/delivered\n\nAllowed only when status is `accepted`.\nIdentifier via query `id` (return request ObjectId).\nThis does **not** complete the return request.\nProvider must still confirm receipt.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Return request ObjectId owned by the authenticated client.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Client delivery marked.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "10 — Favorites",
      "id": "5b520d365b520d365b520d36",
      "description": "KamTeswa API — 10 — Favorites",
      "item": [
        {
          "name": "List client favorite products",
          "id": "1ef4e7281ef4e7281ef4e728",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/client/favorites?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "client",
                "favorites"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList client favorite products\n\nReturns the authenticated client's favorite products as product cards.\n\n- Client bearer token only.\n- Provider tokens are rejected.\n- Pagination uses `page` and `limit` query params.\n- Response `paginate.perPage` mirrors the applied `limit`.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Favorite products list.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `title`, `displayType`, `items`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Add product to favorites",
          "id": "0589303e0589303e0589303e",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/client/favorites?productId=%7B%7BproductId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "client",
                "favorites"
              ],
              "variable": [],
              "query": [
                {
                  "key": "productId",
                  "value": "{{productId}}",
                  "description": "MongoDB ObjectId of the product.\nRequired.\nType: string.\nExample variable: {{productId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nAdd product to favorites\n\nAdds a product to the authenticated client's favorites.\n\n- Idempotent: if already favorited, returns success with `isFavorite: true`.\n- Provider tokens are rejected.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **productId** (required): Product ObjectId consumed by the current validator.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Product favorited (new or already present).\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `productId`, `isFavorite`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Remove product from favorites",
          "id": "2cd4ac0d2cd4ac0d2cd4ac0d",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/client/favorites?productId=%7B%7BproductId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "client",
                "favorites"
              ],
              "variable": [],
              "query": [
                {
                  "key": "productId",
                  "value": "{{productId}}",
                  "description": "MongoDB ObjectId of the product.\nRequired.\nType: string.\nExample variable: {{productId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nRemove product from favorites\n\nRemoves a product from the authenticated client's favorites.\n\n- Idempotent: if not favorited, returns success with `isFavorite: false`.\n- Provider tokens are rejected.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **productId** (required): Product ObjectId consumed by the current validator.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Product unfavorited (removed or already absent).\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `productId`, `isFavorite`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "11 — Notifications",
      "id": "139f71b8139f71b8139f71b8",
      "description": "KamTeswa API — 11 — Notifications",
      "item": [
        {
          "name": "List my notifications",
          "id": "45173b4345173b4345173b43",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notifications?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList my notifications\n\n- Returns only the authenticated user's notifications (client or provider), newest first.\n- The user is resolved from the bearer token; no user id is accepted from the request.\n- Side effect: the returned page is marked seen and the unread counter is reset to 0.\n- Supports `page` and `limit` pagination; the response carries the standard `paginate` block.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page number.\n  - Example: `{{page}}`\n- **limit** (optional): Items per page.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- The authenticated user's notifications, newest first.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Delete all my notifications",
          "id": "14307c2e14307c2e14307c2e",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notifications",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nDelete all my notifications\n\n- Deletes every notification addressed to the authenticated user only.\n- Other users' notifications are never touched; the receiver filter comes from the bearer token.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- All of the authenticated user's notifications were deleted.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Delete one of my notifications",
          "id": "7443a0e57443a0e57443a0e5",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notification?id=%7B%7BnotificationId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notification"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "{{notificationId}}",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample variable: {{notificationId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nDelete one of my notifications\n\n- Deletes a single notification by `id`, only when it is addressed to the authenticated user.\n- An id that does not exist — or belongs to another user — returns the same `fail` response, so notification ids cannot be probed.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Id of the notification to delete. Must belong to the authenticated user.\n  - Example: `665f1c2a9b4e1d0012ab34aa`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- The notification was deleted.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get my unread notifications count",
          "id": "159b9343159b9343159b9343",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notifications/count",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications",
                "count"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet my unread notifications count\n\n- Returns the authenticated user's own unread counter (`notifyCount`) only.\n- The counter is reset to 0 by `GET /notifications`.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Unread notifications count for the authenticated user.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Toggle my notifications on/off",
          "id": "6073b3126073b3126073b312",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notifications/toggle",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications",
                "toggle"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nToggle my notifications on/off\n\n- Flips the authenticated user's own `isNotify` flag (enable/disable push notifications).\n- No body is required or consumed; only the token owner's account is changed.\n- Returns the updated safe profile DTO with the new `isNotify` value.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- isNotify was toggled; the updated profile is returned.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `name`, `avatar`, `countryCode`, `phone`, `fullPhone`, `userType`, `status`, `active`, `isNotify`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Broadcast notification to all active users",
          "id": "33af813533af813533af8135",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/notifications/broadcast",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "notifications",
                "broadcast"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nBroadcast notification to all active users\n\n- Requires an authenticated **provider** bearer token only (`ProviderBearerAuth`).\n- Client bearer tokens are rejected.\n- Sends one in-app notification to every client and provider with `status: active` and `active: true`.\n- The sender is excluded from the recipient list.\n- Push delivery respects each recipient's `isNotify` preference (handled by the notification service).\n- Request body is **multipart/form-data** with a single field: `message` (5–2000).\n- The notification title is always the project name taken from settings; it is not accepted from the request.\n- Notification type stored as `admin` (informational; no deep-link action).\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `message`\nNotification body shown to recipients. The title is always the project name.\nRequired.\nType: string.\nExample: خصم جديد متاح على منتجات المتجر اليوم.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Broadcast accepted; in-app notifications were created for active recipients.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `recipients`, `sent`, `failed`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "message",
                  "type": "text",
                  "description": "Notification body shown to recipients. The title is always the project name.\nRequired.\nType: string.\nExample: خصم جديد متاح على منتجات المتجر اليوم.",
                  "disabled": false,
                  "value": "خصم جديد متاح على منتجات المتجر اليوم."
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "12 — Wallet",
      "id": "273a6848273a6848273a6848",
      "description": "KamTeswa API — 12 — Wallet",
      "item": [
        {
          "name": "Get the current wallet balance",
          "id": "4cb24dfe4cb24dfe4cb24dfe",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/wallet",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "wallet"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet the current wallet balance\n\nReturn the wallet balance for the authenticated client.\n\n- The route is registered after `requireAuth`.\n- `authorize(UserTypeEnum.CLIENT)` rejects provider tokens.\n- The client is resolved from the JWT; no user id is accepted from the request.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Current wallet balance.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `balance`, `balanceText`, `currency`, `currencyCode`, `currencySymbol`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Charge the current wallet",
          "id": "05d83b4d05d83b4d05d83b4d",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/wallet/charge",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "wallet",
                "charge"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nCharge the current wallet\n\nAdd the submitted amount to the authenticated client's wallet.\n\n- The route is registered after `requireAuth`.\n- `authorize(UserTypeEnum.CLIENT)` rejects provider tokens.\n- `price` must be numeric and greater than zero.\n- The current backend immediately increments `user.balance` and writes a\n  `BalanceHistory` charge entry.\n\n**Security warning:** payment-gateway verification is marked TODO in the\ncurrent backend. Executing this operation against production changes the\nreal authenticated account balance immediately.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `price`\nAmount to add to the authenticated client wallet. Must be greater than zero.\nRequired.\nType: number.\nExample: 250\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Wallet balance was increased successfully.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `balance`, `balanceText`, `currency`, `currencyCode`, `currencySymbol`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "price",
                  "type": "text",
                  "description": "Amount to add to the authenticated client wallet. Must be greater than zero.\nRequired.\nType: number.\nExample: 250",
                  "disabled": false,
                  "value": "250"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "13 — Complaints & Contact",
      "id": "1612ea941612ea941612ea94",
      "description": "KamTeswa API — 13 — Complaints & Contact",
      "item": [
        {
          "name": "Send a contact message",
          "id": "2a9f64912a9f64912a9f6491",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Optional Bearer token. Disabled by default for guest access (SecretKeyAuth alone). Enable and use {{clientToken}} or {{providerToken}} when calling as an authenticated actor.",
                "disabled": true
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/contact",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "contact"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSend a contact message\n\nAccepts messages from guests, authenticated clients, and authenticated providers.\nGuest requests must include `name`, `countryCode`, and `phone`; authenticated users\nmay omit profile fields. A supplied bearer token must be valid and is never ignored.\n\n## Actor\nVisitor / Client / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** with SecretKeyAuth for guests.\n- Optional **ClientBearerAuth** or **ProviderBearerAuth**.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: optional Bearer token when calling as an authenticated actor\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `name`\nRequired for guests unless available on the authenticated profile.\nOptional.\nType: string.\nExample: Example User\n\n### `countryCode`\nRequired for guests unless available on the authenticated profile.\nOptional.\nType: string.\nExample variable: {{clientCountryCode}}\n\n### `phone`\nRequired for guests unless available on the authenticated profile.\nOptional.\nType: string.\nExample variable: {{clientPhone}}\n\n### `message`\nFree-text message body.\nRequired.\nType: string.\nExample: أرغب في معرفة المزيد عن الخدمة.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Contact message saved.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `status`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Duplicate phone/email** — registration conflict when the identity already exists.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "name",
                  "type": "text",
                  "description": "Required for guests unless available on the authenticated profile.\nOptional.\nType: string.\nExample: Example User",
                  "disabled": false,
                  "value": "Example User"
                },
                {
                  "key": "countryCode",
                  "type": "text",
                  "description": "Required for guests unless available on the authenticated profile.\nOptional.\nType: string.\nExample variable: {{clientCountryCode}}",
                  "disabled": false,
                  "value": "{{clientCountryCode}}"
                },
                {
                  "key": "phone",
                  "type": "text",
                  "description": "Required for guests unless available on the authenticated profile.\nOptional.\nType: string.\nExample variable: {{clientPhone}}",
                  "disabled": false,
                  "value": "{{clientPhone}}"
                },
                {
                  "key": "message",
                  "type": "text",
                  "description": "Free-text message body.\nRequired.\nType: string.\nExample: أرغب في معرفة المزيد عن الخدمة.",
                  "disabled": false,
                  "value": "أرغب في معرفة المزيد عن الخدمة."
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Create a complaint or suggestion",
          "id": "3ac71b243ac71b243ac71b24",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/complaint",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "complaint"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nCreate a complaint or suggestion\n\nCreates a complaint/suggestion for the authenticated client or provider. Ownership\nis always taken from the bearer token; `userId` and `userType` are not accepted.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `title`\nShort title when required by the endpoint.\nRequired.\nType: string.\nExample: عنوان الشكوى / المقترح\n\n### `message`\nFree-text message body.\nRequired.\nType: string.\nExample: نص الشكوى / المقترح\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Complaint/suggestion saved with pending status.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `number`, `status`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "title",
                  "type": "text",
                  "description": "Short title when required by the endpoint.\nRequired.\nType: string.\nExample: عنوان الشكوى / المقترح",
                  "disabled": false,
                  "value": "عنوان الشكوى / المقترح"
                },
                {
                  "key": "message",
                  "type": "text",
                  "description": "Free-text message body.\nRequired.\nType: string.\nExample: نص الشكوى / المقترح",
                  "disabled": false,
                  "value": "نص الشكوى / المقترح"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List my complaints and suggestions",
          "id": "6970f77a6970f77a6970f77a",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/complaint?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "complaint"
              ],
              "variable": [],
              "query": [
                {
                  "key": "status",
                  "value": "",
                  "description": "Filter or status value as documented by the endpoint.\nOptional.\nType: string.\nAllowed values: pending, in_progress, replied.",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList my complaints and suggestions\n\nReturns only records owned by the authenticated client/provider, newest first.\nOptional status filtering supports exactly `pending`, `in_progress`, and `replied`.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **status** (optional): Filter by complaint/suggestion status.\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated complaints/suggestions owned by the token holder.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get my complaint or suggestion details",
          "id": "39f6ae4739f6ae4739f6ae47",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/complaint/details?id=665f1c2a9b4e1d0012ab34d2",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "complaint",
                "details"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "665f1c2a9b4e1d0012ab34d2",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d2",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nGet my complaint or suggestion details\n\nResolves the record by the required query parameter `id` and returns it only when\nit belongs to the authenticated token holder. Another user's id is returned as not found.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Complaint/Suggestion id.\n  - Example: `665f1c2a9b4e1d0012ab34d2`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Complaint/suggestion details owned by the token holder.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `number`, `title`, `status`, `statusText`, `createdAt`, `message`, `adminReply`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "14 — CMS / Settings",
      "id": "54d17eb854d17eb854d17eb8",
      "description": "KamTeswa API — 14 — CMS / Settings",
      "item": [
        {
          "name": "Get About Us content",
          "id": "40ccab1840ccab1840ccab18",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/about",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "about"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet About Us content\n\nReturns the localized About Us content.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Localized About Us content.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `description`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List frequently asked questions",
          "id": "5d4da6dd5d4da6dd5d4da6dd",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/fqs",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "fqs"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nList frequently asked questions\n\nUses the existing route name `/fqs`.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Localized FAQ list ordered newest first.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get Privacy Policy",
          "id": "0bd795dd0bd795dd0bd795dd",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/privacy",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "privacy"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet Privacy Policy\n\nReturns the localized Privacy Policy content.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Localized privacy policy text.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get Terms and Conditions",
          "id": "3fbf851e3fbf851e3fbf851e",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/terms",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "terms"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet Terms and Conditions\n\nReturns the localized Terms and Conditions content.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Localized terms and conditions content.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `terms`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List application intro screens",
          "id": "3589618c3589618c3589618c",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/intros?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "intros"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList application intro screens\n\nPaginated catalogue of localized intro screens ordered newest first.\nSame screen is used by client and provider onboarding.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated localized intro screens ordered newest first.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get application settings",
          "id": "7d4654eb7d4654eb7d4654eb",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/setting",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "setting"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet application settings\n\nSingle settings document created by the administration (not a paginated list).\nReturns public store links, contact channels, branding assets, and feature flags\nused by the mobile/web clients.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Application settings object.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `linkAndroid`, `linkApple`, `linkWebSite`, `phone`, `phoneWhats`, `email`, `siteNameAr`, `siteNameEn`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "15 — Chat",
      "id": "392941743929417439294174",
      "description": "KamTeswa API — 15 — Chat",
      "item": [
        {
          "name": "List chats",
          "id": "40adf68a40adf68a40adf68a",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/chats?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "chats"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList chats\n\n- Requires an authenticated client or provider bearer token (same OR pattern as `GET /profile`).\n- Uses the bearer token actor type; client and provider credentials are never mixed.\n- Returns the caller's 1:1 chats (`isGroup: false`), sorted by `updatedAt` descending.\n- Each card includes the other member's `name`/`avatar`, plus `lastMessage` preview and `time`.\n- Supports `page` and `limit`; the response carries the standard `paginate` block.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated chat list for the authenticated actor.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List chat messages",
          "id": "70f4478270f4478270f44782",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/chat/messages?id=665f1c2a9b4e1d0012ab34c1&page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "chat",
                "messages"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "665f1c2a9b4e1d0012ab34c1",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34c1",
                  "disabled": false
                },
                {
                  "key": "msgId",
                  "value": "665f1c2a9b4e1d0012ab34d9",
                  "description": "Msg Id field.\nOptional.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d9",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList chat messages\n\n- Requires an authenticated client or provider bearer token (same OR pattern as `GET /profile`).\n- Uses the bearer token actor type; client and provider credentials are never mixed.\n- Requires chat `id` (MongoId). Optional `msgId` filters to messages older than that id (`_id < msgId`).\n- Paginated with `page`/`limit`, sorted by `createdAt` descending inside the filter window.\n- Response `data` is `{ messages, chat }` where `chat` is the header layout (`name`, `avatar`).\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Chat MongoId.\n  - Example: `665f1c2a9b4e1d0012ab34c1`\n- **msgId** (optional): Optional cursor — return only messages older than this message id.\n  - Example: `665f1c2a9b4e1d0012ab34d9`\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated messages plus chat header layout.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `messages`, `chat`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Upload a chat image",
          "id": "1f78f7771f78f7771f78f777",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/chat/upload",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "chat",
                "upload"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nUpload a chat image\n\n- Requires an authenticated client or provider bearer token (same OR pattern as `GET /profile`).\n- Uses the bearer token actor type; client and provider credentials are never mixed.\n- Uploads a single chat image attachment.\n- Multipart fields: `id` (chat MongoId), `file` (image binary).\n- Returns `{ url, file }` — filename only is stored on disk; `url` is the public path.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `id`\nChat MongoId used as the upload folder name.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34c1\n\n### `file`\nImage file uploaded under the form field name `file`.\nRequired.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Image uploaded successfully.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `url`, `file`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "id",
                  "type": "text",
                  "description": "Chat MongoId used as the upload folder name.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34c1",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34c1"
                },
                {
                  "key": "file",
                  "type": "file",
                  "description": "Image file uploaded under the form field name `file`.\nRequired.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "16 — Packages & Subscriptions",
      "id": "6025e55c6025e55c6025e55c",
      "description": "KamTeswa API — 16 — Packages & Subscriptions",
      "item": [
        {
          "name": "List ai-packegs",
          "id": "31cf05c831cf05c831cf05c8",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/ai-packegs?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "ai-packegs"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList ai-packegs\n\n- Requires an authenticated **client or provider** bearer token (`ClientBearerAuth` or `ProviderBearerAuth`).\n- Paginated catalogue of ai-packegs (`Package` model), sorted by `price` then `duration`.\n- Each card includes UI flags: `currentPackage`, `subscribeButton`.\n- Active same package → `currentPackage: true`, `subscribeButton: false`.\n- Expired same package → `currentPackage: true`, `subscribeButton: true` (renew via subscribe).\n- Re-subscribing to the same package while it is still active is rejected.\n- Supports `page` and `limit`; the response carries the standard `paginate` block.\n- Route aliases: `GET /packages-ai` and `GET /packages` remain available for backward compatibility.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated ai-packegs with `currentPackage` and `subscribeButton` flags.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List premuim-packeges",
          "id": "01c29bb401c29bb401c29bb4",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/premuim-packeges?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "premuim-packeges"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList premuim-packeges\n\n- Requires an authenticated **provider** bearer token only (`ProviderBearerAuth`). Clients are not allowed.\n- Paginated catalogue of premuim-packeges (`PremiumPackage` model, `userType: provider`), sorted by `price` then `duration`.\n- Same response shape as AI packages: UI flags `currentPackage`, `subscribeButton`.\n- Active same package → `currentPackage: true`, `subscribeButton: false`.\n- Expired same package → `currentPackage: true`, `subscribeButton: true` (renew via subscribe).\n- Re-subscribing to the same package while it is still active is rejected.\n- Supports `page` and `limit`; the response carries the standard `paginate` block.\n- Route aliases: `GET /packages-premium` and `GET /premium-packages` remain available for backward compatibility.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated premuim-packeges with `currentPackage` and `subscribeButton` flags.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Subscribe to an AI pricing package",
          "id": "0a69b74c0a69b74c0a69b74c",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/subscription",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "subscription"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSubscribe to an AI pricing package\n\n- Requires an authenticated **client or provider** bearer token (`ClientBearerAuth` or `ProviderBearerAuth`).\n- Request body is **multipart/form-data** with `id` (AI `Package` id) and `paymentMethod` (`wallet` | `online`).\n- Optional coupon fields: `coupon` (Coupon id from `PATCH /apply-coupon`) and `totalAfterCoupon` (required when `coupon` is set). Server reloads the coupon, recomputes discount from `package.price`, rejects mismatches beyond ±0.02, charges the discounted amount, stores coupon fields on the subscription, and atomically increments coupon `counter` only when `payableAmount > 0`.\n- Same payment strategies as the rest of More (`wallet` deducts balance; `online` is currently a pass-through stub).\n- Free packages (`isFree: true` or `price: 0`) skip charging and reject coupons.\n- Creates a `Subscription` row (`userType` + `userRef` match the bearer actor) and points `user.subscribe` at it.\n- Re-subscribing to the **same** package while it is still active is rejected.\n- Any previous **active** AI subscriptions for the same user on a **different** package are set to `cancelled` (and their expire cron jobs are cleared) so only one active row exists.\n- When the expire cron runs for the new subscription, its status becomes `expired`.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `id`\nMongoDB id of the AI pricing package (`Package`).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34a1\n\n### `paymentMethod`\nPayment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet\n\n### `coupon`\nOptional Coupon Mongo id from `PATCH /apply-coupon` preview (`data.coupon`). When set, `totalAfterCoupon` is required and the wallet/online charge uses the discounted amount. Coupons are rejected on free packages.\nOptional.\nType: string.\n\n### `totalAfterCoupon`\nRequired when `coupon` is sent. Must match server recomputation within ±0.02 or the request fails with `totalAfterCouponMismatch`.\nOptional.\nType: number.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Subscription created. Previous active AI subscriptions for this user are cancelled.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "id",
                  "type": "text",
                  "description": "MongoDB id of the AI pricing package (`Package`).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34a1",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34a1"
                },
                {
                  "key": "paymentMethod",
                  "type": "text",
                  "description": "Payment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet",
                  "disabled": false,
                  "value": "wallet"
                },
                {
                  "key": "coupon",
                  "type": "text",
                  "description": "Optional Coupon Mongo id from `PATCH /apply-coupon` preview (`data.coupon`). When set, `totalAfterCoupon` is required and the wallet/online charge uses the discounted amount. Coupons are rejected on free packages.\nOptional.\nType: string.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "totalAfterCoupon",
                  "type": "text",
                  "description": "Required when `coupon` is sent. Must match server recomputation within ±0.02 or the request fails with `totalAfterCouponMismatch`.\nOptional.\nType: number.",
                  "disabled": false,
                  "value": ""
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Subscribe to a premium package",
          "id": "7e6b1f6a7e6b1f6a7e6b1f6a",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/premuim-subscription",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "premuim-subscription"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSubscribe to a premium package\n\n- Requires an authenticated **provider** bearer token only (`ProviderBearerAuth`).\n- Request body is **multipart/form-data** with `id` (PremiumPackage id), `paymentMethod` (`wallet` | `online`), and optional `ids` (single product Mongo id, or JSON array of ids).\n- Optional coupon fields: `coupon` + `totalAfterCoupon` (same anti-tamper / consume rules as `POST /subscription`; preview via `PATCH /apply-coupon`).\n- `ids` examples: `6a65c8744274743cf7a7b2bb` or `[\"6a65c8744274743cf7a7b2bb\"]`.\n- Same payment strategies as AI subscribe (`wallet` deducts balance; `online` is a pass-through stub).\n- Free packages (`price: 0`) skip charging and reject coupons.\n- Creates a `PremiumSubscription` row (`userType: provider`) and points `provider.premiumSubscription` at it.\n- If `ids` is sent, every product id must exist and belong to the provider; otherwise the request fails with `400`. Omitting `ids` (or sending empty) is allowed.\n- On success, previously demoted premium products (`premiumSuspended = true`) are restored to `isPremium = true` immediately.\n- On success with valid `ids`, those products are also marked `isPremium = true` (`premiumExpireAt` matches the subscription expiry).\n- Any previous **active** premium subscriptions for the same provider are set to `cancelled` (and their expire cron jobs are cleared) so only one active row exists.\n- When the expire cron runs for the new subscription, its status becomes `expired` and premium products are demoted.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `id`\nMongoDB id of the premium package (`PremiumPackage`).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34b2\n\n### `paymentMethod`\nPayment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet\n\n### `ids`\nOptional. Product Mongo id(s) to mark as premium on success (multipart field). Accepts a single id (`6a65c8744274743cf7a7b2bb`) or a JSON array (`[\"6a65c8744274743cf7a7b2bb\"]`). Missing/empty is allowed. When provided, every id must exist and belong to the authenticated provider or the request fails.\nOptional.\nType: string.\nExample: 6a65c8744274743cf7a7b2bb\n\n### `coupon`\nOptional Coupon Mongo id from `PATCH /apply-coupon`. When set, `totalAfterCoupon` is required.\nOptional.\nType: string.\n\n### `totalAfterCoupon`\nRequired when `coupon` is sent. Must match server recomputation within ±0.02.\nOptional.\nType: number.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Premium subscription created. Previous active premium subscriptions for this provider are cancelled.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "id",
                  "type": "text",
                  "description": "MongoDB id of the premium package (`PremiumPackage`).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34b2",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34b2"
                },
                {
                  "key": "paymentMethod",
                  "type": "text",
                  "description": "Payment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet",
                  "disabled": false,
                  "value": "wallet"
                },
                {
                  "key": "ids",
                  "type": "text",
                  "description": "Optional. Product Mongo id(s) to mark as premium on success (multipart field). Accepts a single id (`6a65c8744274743cf7a7b2bb`) or a JSON array (`[\"6a65c8744274743cf7a7b2bb\"]`). Missing/empty is allowed. When provided, every id must exist and belong to the authenticated provider or the request fails.\nOptional.\nType: string.\nExample: 6a65c8744274743cf7a7b2bb",
                  "disabled": false,
                  "value": "6a65c8744274743cf7a7b2bb"
                },
                {
                  "key": "coupon",
                  "type": "text",
                  "description": "Optional Coupon Mongo id from `PATCH /apply-coupon`. When set, `totalAfterCoupon` is required.\nOptional.\nType: string.",
                  "disabled": false,
                  "value": ""
                },
                {
                  "key": "totalAfterCoupon",
                  "type": "text",
                  "description": "Required when `coupon` is sent. Must match server recomputation within ±0.02.\nOptional.\nType: number.",
                  "disabled": false,
                  "value": ""
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Preview apply coupon on a subscription package",
          "id": "74c830e474c830e474c830e4",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/apply-coupon",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "apply-coupon"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nPreview apply coupon on a subscription package\n\n- Requires an authenticated **client or provider** bearer token (`ClientBearerAuth` or `ProviderBearerAuth`) + `secretkey`.\n- Preview only: validates the coupon and returns discounted totals. Does **not** increment `counter`.\n- Send `code` + `packageId` only. The server resolves `packageId` against AI `Package` first, then `PremiumPackage`.\n- If the id is a premium package, **provider only** (clients get `unauthorized`).\n- Free packages (`isFree` or `price <= 0`) reject coupons.\n- After a successful preview, call `POST /subscription` or `POST /premuim-subscription` with:\n  - `coupon` = `data.coupon`\n  - `totalAfterCoupon` = `data.totalAfterCoupon`\n  - `paymentMethod` = `wallet` | `online`\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `code`\nCoupon code (trimmed, min 6 chars).\nRequired.\nType: string.\nExample variable: {{otpCode}}\nOTP is never returned by the API.\n\n### `packageId`\nMongo id of an AI `Package` or a `PremiumPackage`. Server resolves which collection it belongs to (no `kind` field).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34a1\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Coupon is usable; discounted totals returned (counter unchanged).\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `coupon`, `code`, `packageId`, `price`, `total`, `discount`, `totalAfterCoupon`, `discountText`, `afterDiscountText`, `beforeDiscountText`, `currency`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "code",
                  "type": "text",
                  "description": "Coupon code (trimmed, min 6 chars).\nRequired.\nType: string.\nExample variable: {{otpCode}}\nOTP is never returned by the API.",
                  "disabled": false,
                  "value": "{{otpCode}}"
                },
                {
                  "key": "packageId",
                  "type": "text",
                  "description": "Mongo id of an AI `Package` or a `PremiumPackage`. Server resolves which collection it belongs to (no `kind` field).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34a1",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34a1"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "17 — Settlements",
      "id": "3f07eb503f07eb503f07eb50",
      "description": "KamTeswa API — 17 — Settlements",
      "item": [
        {
          "name": "Get provider due financials",
          "id": "354e3f88354e3f88354e3f88",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/due-financials",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "due-financials"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nGet provider due financials\n\nReturns only eligible financial transactions owned by the authenticated provider.\nEligibility follows the existing backend rule: the related order must be finished,\nthe transaction must be pending or rejected, and it must not already be held by a\nsettlement.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Provider due-financial summary and eligible transaction page.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `totalPriceTranslated`, `totalPriceText`, `totalPrice`, `totalAppCommissionTranslated`, `totalAppCommissionText`, `totalAppCommission`, `totalVatPriceTranslated`, `totalVatPriceText`, `totalVatPrice`, `totalTranslated`, `totalText`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List provider settlements",
          "id": "5938e27b5938e27b5938e27b",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/settlements?status=&page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "settlements"
              ],
              "variable": [],
              "query": [
                {
                  "key": "status",
                  "value": "",
                  "description": "Filter or status value as documented by the endpoint.\nRequired.\nType: string.\nAllowed values: pending, finished.",
                  "disabled": false
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList provider settlements\n\nReturns only settlement requests owned by the authenticated provider, newest first.\n`pending` filters pending requests. `finished` maps to accepted and rejected requests.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **status** (required): Required status filter. `pending` = awaiting admin action. `finished` = accepted or rejected.\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated settlement requests owned by the provider.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Request settlement of all available dues",
          "id": "06c9a71806c9a71806c9a718",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/settlement",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "settlement"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nRequest settlement of all available dues\n\nCreates one pending settlement for all currently eligible provider transactions.\nThe existing backend settles the complete available amount; no `amount`, `userId`,\nor `providerId` field is accepted. Eligible transactions are held before the\nsettlement is created so they cannot be requested again.\nBank account fields (`bankName`, `accountName`, `accountNumber`, `iban`) are\nrequired in the body and persisted on the provider profile.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `bankName`\nProvider bank name.\nRequired.\nType: string.\nExample: الراجحي\n\n### `accountName`\nBank account holder name.\nRequired.\nType: string.\nExample: متجر زفييرا\n\n### `accountNumber`\nBank account number (digits only).\nRequired.\nType: string.\nExample: 1234567890\n\n### `iban`\nIBAN for the provider bank account.\nRequired.\nType: string.\nExample: SA0380000000608010167519\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Settlement request created with pending status.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `settlementNumber`, `amount`, `totalTranslated`, `totalText`, `total`, `status`, `statusText`, `createdAt`, `createdAtIso`, `processedAt`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "bankName",
                  "type": "text",
                  "description": "Provider bank name.\nRequired.\nType: string.\nExample: الراجحي",
                  "disabled": false,
                  "value": "الراجحي"
                },
                {
                  "key": "accountName",
                  "type": "text",
                  "description": "Bank account holder name.\nRequired.\nType: string.\nExample: متجر زفييرا",
                  "disabled": false,
                  "value": "متجر زفييرا"
                },
                {
                  "key": "accountNumber",
                  "type": "text",
                  "description": "Bank account number (digits only).\nRequired.\nType: string.\nExample: 1234567890",
                  "disabled": false,
                  "value": "1234567890"
                },
                {
                  "key": "iban",
                  "type": "text",
                  "description": "IBAN for the provider bank account.\nRequired.\nType: string.\nExample: SA0380000000608010167519",
                  "disabled": false,
                  "value": "SA0380000000608010167519"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get provider settlement details",
          "id": "6037f8ae6037f8ae6037f8ae",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/settlement?id=665f1c2a9b4e1d0012ab34d0",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "settlement"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "665f1c2a9b4e1d0012ab34d0",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nGet provider settlement details\n\nResolves the settlement by the required query parameter `id` and returns it only\nwhen it belongs to the authenticated provider.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Settlement Mongo ObjectId.\n  - Example: `665f1c2a9b4e1d0012ab34d0`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Settlement details owned by the authenticated provider.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `totalPrice`, `totalAppCommission`, `totalVatPrice`, `total`, `currency`, `financials`, `settelmentDebitBtn`, `status`, `statusText`, `settlementNumber`, `amount`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "18 — AI Pricing",
      "id": "24765d8724765d8724765d87",
      "description": "KamTeswa API — 18 — AI Pricing",
      "item": [
        {
          "name": "Create an AI pricing request",
          "id": "012771070127710701277107",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/pricing-request",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "pricing-request"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nCreate an AI pricing request\n\nSubmit product name, image, and specifications for AI pricing (Price Request Mechanism).\n\n- Requires an authenticated **client or provider** bearer token (`ClientBearerAuth` or `ProviderBearerAuth`).\n- Swagger UI exposes a **token switcher** (Client / Provider); only the selected scheme is sent.\n- Multipart body: `name` (required), `image` (required jpeg/jpg/png/webp), `specifications` (optional).\n- Without a subscription: **1 free pricing request per calendar day** (soft-deleted requests still count for that day).\n- A second attempt the same day without an active AI subscription returns key `needSubscribe` (HTTP 400).\n- With an active AI package subscription (`POST /subscription`), pricing requests are unlimited.\n- AI engine is currently stubbed with a temporary random price; replace the stub in `PricingRequest.runAiPricingEngine` when the real AI API is connected.\n- The created row is saved into the pricing library.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `name`\nDisplay name for the account or contact.\nRequired.\nType: string.\nExample: آيفون 13\n\n### `specifications`\nSpecifications field.\nOptional.\nType: string.\nExample: اللون أسود - الحالة جديد - الذاكرة 256\n\n### `image`\nImage file upload.\nRequired.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Pricing completed (stub/random until AI is wired). Shape matches `returnObj.pricingRequestItem` (create view).\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `name`, `image`, `specifications`, `suggestedPrice`, `suggestedPriceTXT`, `priceRangeMin`, `priceRangeMax`, `currency`, `createdAt`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "name",
                  "type": "text",
                  "description": "Display name for the account or contact.\nRequired.\nType: string.\nExample: آيفون 13",
                  "disabled": false,
                  "value": "آيفون 13"
                },
                {
                  "key": "specifications",
                  "type": "text",
                  "description": "Specifications field.\nOptional.\nType: string.\nExample: اللون أسود - الحالة جديد - الذاكرة 256",
                  "disabled": false,
                  "value": "اللون أسود - الحالة جديد - الذاكرة 256"
                },
                {
                  "key": "image",
                  "type": "file",
                  "description": "Image file upload.\nRequired.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List pricing library",
          "id": "5b44fd4f5b44fd4f5b44fd4f",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/pricing-library?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "pricing-library"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList pricing library\n\nPaginated list of the authenticated user's AI pricing requests saved in the pricing library (مكتبة التسعير).\n\n- Requires an authenticated **client or provider** bearer token (`ClientBearerAuth` or `ProviderBearerAuth`).\n- Swagger UI exposes a **token switcher** (Client / Provider); only the selected scheme is sent.\n- Filters: `isDeleted: false`, `isHidden: false`, `savedToLibrary: true`, owned by `req.user`.\n- Each card includes `detailsButton` and `deleteButton`.\n- Soft-deleted items (`DELETE /pricing-library/delete`) never appear here.\n- Sorted newest first via `ApiFeature`.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Pricing library page.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Pricing library item details",
          "id": "56b9927e56b9927e56b9927e",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/pricing-library/details?id=665f1c2a9b4e1d0012ab34b1",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "pricing-library",
                "details"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "665f1c2a9b4e1d0012ab34b1",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34b1",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nPricing library item details\n\nFull details for one pricing request owned by the authenticated user (تفاصيل طلب التسعير).\n\n- Requires an authenticated **client or provider** bearer token (`ClientBearerAuth` or `ProviderBearerAuth`).\n- Swagger UI exposes a **token switcher** (Client / Provider); only the selected scheme is sent.\n- Query param `id` is the PricingRequest Mongo id.\n- Returns the AI suggested price and range from `@returnObj.pricingRequestItem` (same base fields as create).\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Id query parameter.\n  - Example: `665f1c2a9b4e1d0012ab34b1`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Pricing request details.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `name`, `image`, `specifications`, `suggestedPrice`, `suggestedPriceTXT`, `priceRangeMin`, `priceRangeMax`, `currency`, `createdAt`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Soft-delete a pricing library item",
          "id": "1889c6201889c6201889c620",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/pricing-library/delete?id=665f1c2a9b4e1d0012ab34b1",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "pricing-library",
                "delete"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "665f1c2a9b4e1d0012ab34b1",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34b1",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nSoft-delete a pricing library item\n\nSoft-deletes one pricing request owned by the authenticated user (`isDeleted: true`).\n\n- Requires an authenticated **client or provider** bearer token (`ClientBearerAuth` or `ProviderBearerAuth`).\n- Swagger UI exposes a **token switcher** (Client / Provider); only the selected scheme is sent.\n- Query param `id` is the PricingRequest Mongo id.\n- Soft-deleted rows are excluded from `GET /pricing-library` and `GET /pricing-library/details`.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Id query parameter.\n  - Example: `665f1c2a9b4e1d0012ab34b1`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Pricing request soft-deleted.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "99 — Utilities",
      "id": "7d4ae16a7d4ae16a7d4ae16a",
      "description": "KamTeswa API — 99 — Utilities",
      "item": [
        {
          "name": "List active product report reasons",
          "id": "40a54fb940a54fb940a54fb9",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/product-report-reasons",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "product-report-reasons"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nList active product report reasons\n\nReturns the active, non-deleted report-reason catalogue in the requested\nresponse language. This operation requires the application `secretkey`\nonly and accepts no query parameters.\n\nThe icon is always a usable URL. When an administrator did not upload an\nicon, or the configured local file is missing, the server returns the\nbundled product-report fallback icon.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Active reasons returned in display order.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `reasons`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List product attributes",
          "id": "73ff18dc73ff18dc73ff18dc",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/attributes?page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "attributes"
              ],
              "variable": [],
              "query": [
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList product attributes\n\nPublic catalogue of the active product attributes created by the administration\n(id + name only). Each attribute's selectable sizes and colors are fetched via\nthe dedicated `/attributes/sizes?id=...` and `/attributes/colors?id=...` endpoints.\n\nProviders pick an existing attribute and its values when publishing a product;\nthey never create attributes or values themselves.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated list of active attributes.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List a single attribute's colors",
          "id": "59a3e9fd59a3e9fd59a3e9fd",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/attributes/colors?id=665f1c2a9b4e1d0012ab34e7&page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "attributes",
                "colors"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "665f1c2a9b4e1d0012ab34e7",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34e7",
                  "disabled": false
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList a single attribute's colors\n\nPaginated colors (`kind = color`) of one attribute, oldest first.\nEach item carries a HEX `colorCode`.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Attribute id. Must reference an existing active attribute.\n  - Example: `665f1c2a9b4e1d0012ab34e7`\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated list of the attribute's active colors.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "List a single attribute's sizes",
          "id": "651e1661651e1661651e1661",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/attributes/sizes?id=665f1c2a9b4e1d0012ab34e7&page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "attributes",
                "sizes"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "665f1c2a9b4e1d0012ab34e7",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34e7",
                  "disabled": false
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList a single attribute's sizes\n\nPaginated sizes (`kind = size`) of one attribute, oldest first.\n\n## Actor\nVisitor\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **Public** (SecretKeyAuth only). No bearer token required.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Attribute id. Must reference an existing active attribute.\n  - Example: `665f1c2a9b4e1d0012ab34e7`\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Paginated list of the attribute's active sizes.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Other — Account Roles",
      "id": "74c05f7e74c05f7e74c05f7e",
      "description": "KamTeswa API — Other — Account Roles",
      "item": [
        {
          "name": "Add a pending provider role request to the current client account",
          "id": "1115ddfc1115ddfc1115ddfc",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/account/provider-request",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "account",
                "provider-request"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nAdd a pending provider role request to the current client account\n\nRequires Client token. Creates a pending Provider request for the current\nClient AccountIdentity. The ProviderMeta request is stored under the client's\nexisting AccountIdentity. Identity fields are copied from AccountIdentity and cannot\nbe supplied by the caller. No Provider or new AccountIdentity is created,\nno password is accepted, and provider mode remains unavailable until approval.\nWhen an active super administrator exists, the successful request also dispatches\na dashboard review notification linked to the new ProviderMeta request.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `avatar`\nOptional image validated by file signature: jpg, jpeg, png, webp.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.\n\n### `nationalId`\nNational Id field.\nOptional.\nType: string.\nExample: 1000000000\n\n### `city`\nCity field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34cf\n\n### `commercialRegisterImage`\nOptional files validated by signature: jpg, jpeg, png, pdf.\nOptional.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.\n\n### `whatsappCountryCode`\nWhatsapp Country Code field.\nOptional.\nType: string.\nExample: +966\n\n### `whatsappNumber`\nWhatsapp Number field.\nRequired.\nType: string.\nExample: 0551234567\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Provider request created and linked to the existing identity.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `providerRequestId`, `accountMode`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "avatar",
                  "type": "file",
                  "description": "Optional image validated by file signature: jpg, jpeg, png, webp.\nOptional.\nType: file.\nSend as a file field. Prefer PNG / JPG / JPEG / WEBP when image validation applies.",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "nationalId",
                  "type": "text",
                  "description": "National Id field.\nOptional.\nType: string.\nExample: 1000000000",
                  "disabled": false,
                  "value": "1000000000"
                },
                {
                  "key": "city",
                  "type": "text",
                  "description": "City field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34cf",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34cf"
                },
                {
                  "key": "commercialRegisterImage",
                  "type": "file",
                  "description": "Optional files validated by signature: jpg, jpeg, png, pdf.\nOptional.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "whatsappCountryCode",
                  "type": "text",
                  "description": "Whatsapp Country Code field.\nOptional.\nType: string.\nExample: +966",
                  "disabled": false,
                  "value": "+966"
                },
                {
                  "key": "whatsappNumber",
                  "type": "text",
                  "description": "Whatsapp Number field.\nRequired.\nType: string.\nExample: 0551234567",
                  "disabled": false,
                  "value": "0551234567"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Add a client role to the current provider account",
          "id": "204380b9204380b9204380b9",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/account/client-role",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "account",
                "client-role"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nAdd a client role to the current provider account\n\nRequires Provider token. Adds a Client role to the current Provider\nAccountIdentity. Creates a Client profile under the provider's existing\nAccountIdentity. The request has no body and never accepts a password.\nNo new AccountIdentity is created. An accepted provider keeps provider mode;\na pending provider receives client mode only.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Client role and profile created under the existing identity.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `clientId`, `accountMode`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Switch the current AccountIdentity active mode",
          "id": "69002eab69002eab69002eab",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/account/switch-mode",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "account",
                "switch-mode"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSwitch the current AccountIdentity active mode\n\nRequires Client or Provider token. Changes `AccountIdentity.activeMode`\nand issues a fresh access token for the selected active profile. It never\nasks for a password, creates a role/profile/AccountIdentity, or issues a new Provider. Client mode\nrequires an active linked Client. Provider mode requires an accepted, active,\nlinked Provider. Pending, rejected, disabled, blocked, deleted, or missing\nProvider profiles cannot enter provider mode. A successful response hydrates\nthe active safe profile DTO directly in `data`: a Client DTO in client mode\nor a Provider DTO in provider mode. It does not add client/provider wrappers\nand returns the new token as `token` with `tokenType=access`. The caller\nmust replace its current session token after a successful switch.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `mode`\nTarget mode only. No password is accepted. Provider mode is available\nonly for an accepted, active, linked Provider profile.\nRequired.\nType: string.\nAllowed values: client, provider.\nExample: client\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Active mode changed and the matching safe active profile was returned.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `currentRole`, `id`, `name`, `avatar`, `countryCode`, `phone`, `fullPhone`, `userType`, `status`, `statusText`, `active`, `token`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "mode",
                  "type": "text",
                  "description": "Target mode only. No password is accepted. Provider mode is available\nonly for an accepted, active, linked Provider profile.\nRequired.\nType: string.\nAllowed values: client, provider.\nExample: client",
                  "disabled": false,
                  "value": "client"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  "",
                  "",
                  "const token =",
                  "  (json && json.data && typeof json.data.token === \"string\" && json.data.token) ||",
                  "  null;",
                  "const currentRole =",
                  "  (json && json.data && typeof json.data.currentRole === \"string\" && json.data.currentRole) ||",
                  "  null;",
                  "",
                  "if (json.key === \"success\") {",
                  "  pm.test(\"Fresh switch token matches the active role\", function () {",
                  "    pm.expect(token).to.be.a(\"string\").and.not.empty;",
                  "    pm.expect([\"client\", \"provider\"]).to.include(currentRole);",
                  "  });",
                  "}",
                  "",
                  "if (json.key === \"success\" && typeof token === \"string\" && token.length > 0) {",
                  "  if (currentRole === \"client\") {",
                  "    pm.environment.set(\"clientToken\", token);",
                  "  } else if (currentRole === \"provider\") {",
                  "    pm.environment.set(\"providerToken\", token);",
                  "  }",
                  "}",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Other — Client Auctions",
      "id": "34fac2fd34fac2fd34fac2fd",
      "description": "KamTeswa API — Other — Client Auctions",
      "item": [
        {
          "name": "Join an Upcoming Auction and pay its server-calculated deposit",
          "id": "528dc4fa528dc4fa528dc4fa",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions/join",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions",
                "join"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nJoin an Upcoming Auction and pay its server-calculated deposit\n\nClient only. Wallet payment is committed transactionally and idempotently.\nOnline returns `202` with a pending AuctionPayment; participation is not\nactivated until a verifiable gateway confirmation exists.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `auctionId`\nAuction Id field.\nRequired.\nType: string.\nExample: 64f001122334455667788990\n\n### `paymentMethod`\nPayment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Wallet deposit paid and participation confirmed\n- Envelope: `{ key, message, status, data? }` — branch on `key`, not only HTTP status.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "auctionId",
                  "type": "text",
                  "description": "Auction Id field.\nRequired.\nType: string.\nExample: 64f001122334455667788990",
                  "disabled": false,
                  "value": "64f001122334455667788990"
                },
                {
                  "key": "paymentMethod",
                  "type": "text",
                  "description": "Payment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet",
                  "disabled": false,
                  "value": "wallet"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Pay the winner's server-calculated remaining amount",
          "id": "494cca48494cca48494cca48",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated client. Filled from {{clientToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions/winner-payment",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions",
                "winner-payment"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nPay the winner's server-calculated remaining amount\n\nWinning Client only. Amount is never accepted from the request. Wallet\nsettlement is transactional and idempotent. Online creates a pending\nAuctionPayment and cannot mark the Auction paid without gateway proof.\nA successful Wallet settlement creates exactly one productless Order linked\nby `auction`, using the Auction name and images as its immutable snapshot.\n\n## Actor\nClient\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** — required.\n  - `Authorization: Bearer {{clientToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `auctionId`\nAuction Id field.\nRequired.\nType: string.\nExample: 64f001122334455667788990\n\n### `paymentMethod`\nPayment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Wallet payment committed, one Auction Order created, and Auction completed\n- Envelope: `{ key, message, status, data? }` — branch on `key`, not only HTTP status.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "auctionId",
                  "type": "text",
                  "description": "Auction Id field.\nRequired.\nType: string.\nExample: 64f001122334455667788990",
                  "disabled": false,
                  "value": "64f001122334455667788990"
                },
                {
                  "key": "paymentMethod",
                  "type": "text",
                  "description": "Payment Method field.\nRequired.\nType: string.\nAllowed values: wallet, online.\nExample: wallet",
                  "disabled": false,
                  "value": "wallet"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Other — Provider Auctions",
      "id": "429635974296359742963597",
      "description": "KamTeswa API — Other — Provider Auctions",
      "item": [
        {
          "name": "Create a productless Auction for admin review",
          "id": "358ae14c358ae14c358ae14c",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auction",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auction"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nCreate a productless Auction for admin review\n\nProvider only. Images are validated by file signature. The server persists\n`Pending`, calculates `endDate = startDate + durationHours`, records history,\nand notifies an active super admin. Product association is not accepted.\nFinancial fields are not accepted from the Provider. `vatPrice` and\n`appCommission` are initialized to null for later financial processing.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `name`\nDisplay name for the account or contact.\nRequired.\nType: string.\nExample: مزاد أجهزة احترافية\n\n### `description`\nLonger text description when required by the endpoint.\nRequired.\nType: string.\nExample: وصف واضح للمزاد\n\n### `images`\nProduct images submitted as files through multipart/form-data (1–10).\nRequired.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.\nExample: \n\n### `depositAmount`\nDeposit Amount field.\nRequired.\nType: number.\nExample: 100\n\n### `startDate`\nMust be a future ISO-8601 date-time, for example 2030-01-01T12:00:00Z. The server calculates endDate from this value and durationHours.\nRequired.\nType: string.\nExample: 2030-01-01T12:00:00Z\n\n### `durationHours`\nDuration Hours field.\nRequired.\nType: integer.\nExample: 6\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Auction created as Pending\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Missing file** — required upload field not attached.\n- **Invalid file type** — file signature rejected (use supported image formats).\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "name",
                  "type": "text",
                  "description": "Display name for the account or contact.\nRequired.\nType: string.\nExample: مزاد أجهزة احترافية",
                  "disabled": false,
                  "value": "مزاد أجهزة احترافية"
                },
                {
                  "key": "description",
                  "type": "text",
                  "description": "Longer text description when required by the endpoint.\nRequired.\nType: string.\nExample: وصف واضح للمزاد",
                  "disabled": false,
                  "value": "وصف واضح للمزاد"
                },
                {
                  "key": "images",
                  "type": "file",
                  "description": "Product images submitted as files through multipart/form-data (1–10).\nRequired.\nType: file[].\nRepeat this multipart file field for each upload supported by the endpoint.\nExample: ",
                  "disabled": false,
                  "src": []
                },
                {
                  "key": "depositAmount",
                  "type": "text",
                  "description": "Deposit Amount field.\nRequired.\nType: number.\nExample: 100",
                  "disabled": false,
                  "value": "100"
                },
                {
                  "key": "startDate",
                  "type": "text",
                  "description": "Must be a future ISO-8601 date-time, for example 2030-01-01T12:00:00Z. The server calculates endDate from this value and durationHours.\nRequired.\nType: string.\nExample: 2030-01-01T12:00:00Z",
                  "disabled": false,
                  "value": "2030-01-01T12:00:00Z"
                },
                {
                  "key": "durationHours",
                  "type": "text",
                  "description": "Duration Hours field.\nRequired.\nType: integer.\nExample: 6",
                  "disabled": false,
                  "value": "6"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Set the opening price of an owned Accepted Auction",
          "id": "73d4b1d673d4b1d673d4b1d6",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions/update",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions",
                "update"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nSet the opening price of an owned Accepted Auction\n\nProvider only. The multipart body must contain exactly `id` and\n`openingPrice`. The Auction must be owned by the authenticated Provider,\nmust still be Accepted, and must not have been published. A successful\nupdate publishes it immediately and changes its exposed status to Upcoming.\n`openingPrice` cannot be lower than `adminMinimumOpeningPrice`.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `id`\nResource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample: 64f001122334455667788990\n\n### `openingPrice`\nProvider opening price; must be at least adminMinimumOpeningPrice.\nRequired.\nType: number.\nExample: 1000\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Auction updated; Accepted becomes Upcoming after configuration\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `status`, `openingPrice`, `adminMinimumOpeningPrice`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "id",
                  "type": "text",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample: 64f001122334455667788990",
                  "disabled": false,
                  "value": "64f001122334455667788990"
                },
                {
                  "key": "openingPrice",
                  "type": "text",
                  "description": "Provider opening price; must be at least adminMinimumOpeningPrice.\nRequired.\nType: number.\nExample: 1000",
                  "disabled": false,
                  "value": "1000"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Cancel an owned Auction before it starts",
          "id": "26849db626849db626849db6",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions/cancel",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions",
                "cancel"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nCancel an owned Auction before it starts\n\nProvider only. Allowed in Pending, Accepted, or Upcoming before start.\nAuction cancellation and all paid-deposit refunds commit in one transaction;\neach refund remains idempotent and has an independent financial record.\naffected Clients, Provider, and admin receive enriched notifications.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `auctionId`\nAuction Id field.\nRequired.\nType: string.\nExample: 64f001122334455667788990\n\n### `reason`\nReason field.\nRequired.\nType: string.\nExample: تعذر استكمال المزاد\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Auction cancelled and all due refunds completed/replayed\n- Envelope: `{ key, message, status, data? }` — branch on `key`, not only HTTP status.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "auctionId",
                  "type": "text",
                  "description": "Auction Id field.\nRequired.\nType: string.\nExample: 64f001122334455667788990",
                  "disabled": false,
                  "value": "64f001122334455667788990"
                },
                {
                  "key": "reason",
                  "type": "text",
                  "description": "Reason field.\nRequired.\nType: string.\nExample: تعذر استكمال المزاد",
                  "disabled": false,
                  "value": "تعذر استكمال المزاد"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Finalize an owned live Auction after its end time",
          "id": "1e5e98f51e5e98f51e5e98f5",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions/end",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions",
                "end"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nFinalize an owned live Auction after its end time\n\nProvider fallback for the server finalizer. It freezes the highest bid,\nrefunds losing deposits, records `Finished`, and notifies the Provider and\nhighest bidder. The winner is not final until Provider acceptance.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `auctionId`\nAuction Id field.\nRequired.\nType: string.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Finalized or stable idempotent replay\n- Envelope: `{ key, message, status, data? }` — branch on `key`, not only HTTP status.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "auctionId",
                  "type": "text",
                  "description": "Auction Id field.\nRequired.\nType: string.",
                  "disabled": false,
                  "value": ""
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Accept the frozen highest bid",
          "id": "41045ed441045ed441045ed4",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions/accept-winner",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions",
                "accept-winner"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nAccept the frozen highest bid\n\nProvider only. The backend computes `remainingAmount = winningBidAmount -\ndepositAmount`, assigns the winner once, opens payment, records status\nhistory, and notifies the winner and admin.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `auctionId`\nAuction Id field.\nRequired.\nType: string.\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Winner accepted and payment is AwaitingPayment\n- Envelope: `{ key, message, status, data? }` — branch on `key`, not only HTTP status.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "auctionId",
                  "type": "text",
                  "description": "Auction Id field.\nRequired.\nType: string.",
                  "disabled": false,
                  "value": ""
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Reject the frozen highest bid once",
          "id": "4e1da7a34e1da7a34e1da7a3",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions/reject-winner",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions",
                "reject-winner"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nReject the frozen highest bid once\n\nProvider only. Records WinnerRejected, reason, and deposit refund without\nauto-selecting a second winner. Notifies the bidder and admin.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `auctionId`\nAuction Id field.\nRequired.\nType: string.\nExample: 64f001122334455667788990\n\n### `reason`\nReason field.\nRequired.\nType: string.\nExample: قيمة العرض غير مناسبة\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Highest bid rejected and held deposit refunded/replayed\n- Envelope: `{ key, message, status, data? }` — branch on `key`, not only HTTP status.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "auctionId",
                  "type": "text",
                  "description": "Auction Id field.\nRequired.\nType: string.\nExample: 64f001122334455667788990",
                  "disabled": false,
                  "value": "64f001122334455667788990"
                },
                {
                  "key": "reason",
                  "type": "text",
                  "description": "Reason field.\nRequired.\nType: string.\nExample: قيمة العرض غير مناسبة",
                  "disabled": false,
                  "value": "قيمة العرض غير مناسبة"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Other — Provider Order",
      "id": "38ad8e5938ad8e5938ad8e59",
      "description": "KamTeswa API — Other — Provider Order",
      "item": [
        {
          "name": "Accept an order",
          "id": "2df102a02df102a02df102a0",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/order/accept",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "order",
                "accept"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nAccept an order\n\nProvider accepts a `new`, unpaid, not-yet-accepted order. A payment timeout\nis scheduled for the client.\n\n- Provider bearer token only.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `orderId`\nOrder Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Order accepted.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "orderId",
                  "type": "text",
                  "description": "Order Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34d0"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Reject an order",
          "id": "4b12c1374b12c1374b12c137",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/order/reject",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "order",
                "reject"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nReject an order\n\nProvider rejects a `new`, unpaid order (including an already-accepted but\nunpaid one). Reserved stock is restored and any scheduled payment timeout\nis cleared.\n\n- Provider bearer token only.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `orderId`\nOrder Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0\n\n### `reason`\nReason ObjectId from the shared reasons catalogue.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34aa\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Order rejected.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "orderId",
                  "type": "text",
                  "description": "Order Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34d0"
                },
                {
                  "key": "reason",
                  "type": "text",
                  "description": "Reason ObjectId from the shared reasons catalogue.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34aa",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34aa"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Mark order delivered to customer",
          "id": "4e94f4144e94f4144e94f414",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/order/delivered?orderId=665f1c2a9b4e1d0012ab34d0",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "order",
                "delivered"
              ],
              "variable": [],
              "query": [
                {
                  "key": "orderId",
                  "value": "665f1c2a9b4e1d0012ab34d0",
                  "description": "Order Id field.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34d0",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nMark order delivered to customer\n\nProvider confirms that the product/order was delivered to the client.\n\n- Provider bearer token only.\n- Allowed when the order is paid, `current`, and deliverable\n  (`processing` or legacy `delivered_to_shipping`).\n- Sets `currentStep=delivered_to_customer` and `deliveredAt`.\n- Does **not** complete/finish the order. Client confirmation is required.\n- Unpaid, cancelled, finished, or already-delivered orders are rejected.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **orderId** (required): Order Id query parameter.\n  - Example: `665f1c2a9b4e1d0012ab34d0`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Order marked delivered; awaiting client receipt confirmation.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Accept a return request",
          "id": "165874ee165874ee165874ee",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/return-request/accept?id=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "return-request",
                "accept"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nAccept a return request\n\nAllowed only when status is `pending_review`.\nIdentifier via query `id` (return request ObjectId).\n\nWallet-paid orders:\n- refund `order.total` to client wallet exactly once\n- create one BalanceHistory charge linked to the return request\n- set refundStatus=`refunded`\n\nOnline-paid orders:\n- do **not** invent a gateway refund\n- set refundStatus=`pending_manual`\n\nAcceptance alone does **not** complete the return.\nClient then sees `canClientMarkDelivered=true`.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Return request ObjectId owned by the authenticated provider.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Return request accepted.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Reject a return request",
          "id": "337a3385337a3385337a3385",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/return-request/reject",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "return-request",
                "reject"
              ],
              "variable": [],
              "query": []
            },
            "description": "## Purpose\nReject a return request\n\nAllowed only when status is `pending_review`.\nBody fields: `id` (return request ObjectId) + `reason` (catalogue Reason ObjectId), same pattern as PATCH /order/reject (`orderId` + `reason`).\nNo refund is issued.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nContent type / mode: **formdata**.\n\n### `id`\nReturn request ObjectId owned by the authenticated provider.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34e1\n\n### `reason`\nReason ObjectId from the shared reasons catalogue.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34aa\n\nMultipart notes:\n- Use Postman **file** type for binary fields.\n- Image uploads typically accept PNG / JPG / JPEG / WEBP when validation applies.\n\n## Query Parameters\nNo query parameters for this endpoint.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Return request rejected.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "id",
                  "type": "text",
                  "description": "Return request ObjectId owned by the authenticated provider.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34e1",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34e1"
                },
                {
                  "key": "reason",
                  "type": "text",
                  "description": "Reason ObjectId from the shared reasons catalogue.\nRequired.\nType: string.\nExample: 665f1c2a9b4e1d0012ab34aa",
                  "disabled": false,
                  "value": "665f1c2a9b4e1d0012ab34aa"
                }
              ]
            }
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Mark returned product as received",
          "id": "31cc484731cc484731cc4847",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/return-request/received?id=",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "return-request",
                "received"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nMark returned product as received\n\nAllowed only when status is `client_delivered`.\nIdentifier via query `id` (return request ObjectId).\nThis is the **final** step and sets status=`completed`.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Return request ObjectId owned by the authenticated provider.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Return request completed.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Other — Provider Products",
      "id": "324ce395324ce395324ce395",
      "description": "KamTeswa API — Other — Provider Products",
      "item": [
        {
          "name": "Mark an owned accepted product as premium",
          "id": "7bc6531d7bc6531d7bc6531d",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{providerToken}}",
                "description": "Bearer token for the authenticated provider. Filled from {{providerToken}} after login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/products/premium?productId=%7B%7BproductId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "products",
                "premium"
              ],
              "variable": [],
              "query": [
                {
                  "key": "productId",
                  "value": "{{productId}}",
                  "description": "MongoDB ObjectId of the product.\nRequired.\nType: string.\nExample variable: {{productId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nMark an owned accepted product as premium\n\n## Purpose\nMarks one owned product as premium (`isPremium = true`) when the provider\nhas an active premium package subscription. Used after selecting a product\nfrom the non-premium list / feature flow on Mobile and Web.\n\n## Actor\nProvider.\n\n## Authorization\nSecretKeyAuth + ProviderBearerAuth.\n\n## Headers\n- `secretkey`\n- `Authorization: Bearer `\n- `lang`\n\n## Request\nQuery `productId` only (required Mongo ObjectId).\n\n## Preconditions (validated before the handler runs)\n- Product exists, is owned by the authenticated provider, and is not soft-deleted\n- `approvalStatus = accept` (administration-approved)\n- `isPremium = false`\n- Provider has an **active** premium package subscription (`status = active`\n  and `expireAt` missing or in the future)\n\nThe handler only sets `isPremium = true` (plus `premiumSuspended = false` and\n`premiumExpireAt` from the validated subscription).\n\n## Success response\nHTTP 200. Message-only envelope `{ key, message, status }` where `key` is\n`success`. The response intentionally has no `data` field. Clients must\nbranch on `key`, not only HTTP status.\n\nOn success the product is updated to `isPremium = true`,\n`premiumSuspended = false`, and `premiumExpireAt` from the active\nsubscription (or `null` when open-ended).\n\n## Common failures\n- Missing / invalid `productId`\n- Product missing, deleted, or not owned by the provider\n- Product already premium\n- Product not administration-accepted (`approvalStatus != accept`)\n- No active premium package subscription\n- Unauthorized / invalid secret / non-provider token\n\n## QA notes\n- Companion list: `GET /provider/products/non-premium`.\n- Does not change moderation or visibility (`isHidden`).\n- See `docs/PRODUCTS_API_CONTRACT.md`.\n\n## Actor\nProvider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ProviderBearerAuth** — required.\n  - `Authorization: Bearer {{providerToken}}`\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **productId** (required): Required MongoDB ObjectId of the product to act on (24-hex).\nUsed by Edit (`PATCH /products`), delete, details, visibility, and premium.\nOwnership is derived from the provider bearer token — do not send a foreign id.\nThis is a **query** parameter on `/api/products`, not a path `/api/products/{id}`.\n  - Example: `665f1c2a9b4e1d0012ab34d0`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Product marked premium. Message-only response with no `data` field; clients branch on `key: success`.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Other — Shared Auctions",
      "id": "4579e85d4579e85d4579e85d",
      "description": "KamTeswa API — Other — Shared Auctions",
      "item": [
        {
          "name": "List auctions for the authenticated Client or Provider",
          "id": "0f1077550f1077550f107755",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions?type=upcoming&page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions"
              ],
              "variable": [],
              "query": [
                {
                  "key": "type",
                  "value": "upcoming",
                  "description": "Filter type as documented by the endpoint.\nRequired.\nType: string.\nAllowed values: pending, accepted, rejected, upcoming, current, finished.\nExample: upcoming",
                  "disabled": false
                },
                {
                  "key": "search",
                  "value": "",
                  "description": "Search field.\nOptional.\nType: string.",
                  "disabled": true
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList auctions for the authenticated Client or Provider\n\nOne role-aware endpoint. The bearer token selects the actor and allowed tabs.\nThe `type` query value is required. Client values: `upcoming`, `current`,\n`finished`. Provider values: `pending`, `accepted`, `rejected`, `upcoming`,\n`current`, `finished`. List DTOs are card-sized;\nthe API does not call the details endpoint per row.\nAn approved Auction enters the Client `upcoming` tab when its dashboard-scheduled\npublication job becomes due. The deadline is the later of approval time and\n`startAt - auctionPublishBeforeStartHours`. At `startAt`, the start job changes\nthe lifecycle status to `current` (the internal live workflow) and creates the\npersistent end job for `endAt`.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **type** (required): Clients may use upcoming/current/finished; Providers may use all six values.\n  - Example: `upcoming`\n- **search** (optional): Search query parameter.\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Role-filtered paginated Auction cards\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get role-aware Auction details",
          "id": "789281887892818878928188",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions/details?id=%7B%7BauctionId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions",
                "details"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "{{auctionId}}",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample variable: {{auctionId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nGet role-aware Auction details\n\nUses the authenticated role and Auction `id` query value. Client responses include own\nparticipation/result/payment state. Provider responses include moderation\nminimum and owner actions. A Client cannot open the Auction before its publication\njob is due. Backend authorization enforces every action.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Id query parameter.\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Role-aware Auction operational profile\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `name`, `status`, `depositAmount`, `openingPrice`, `adminMinimumOpeningPrice`, `startDate`, `endDate`, `durationHours`, `participation`, `allowedActions`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get privacy-safe Auction bid history",
          "id": "7f7249907f7249907f724990",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/auctions/bids?id=%7B%7BauctionId%7D%7D&page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "auctions",
                "bids"
              ],
              "variable": [],
              "query": [
                {
                  "key": "id",
                  "value": "{{auctionId}}",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample variable: {{auctionId}}",
                  "disabled": false
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nGet privacy-safe Auction bid history\n\nDistinct supporting read. Bidder labels are anonymized; tokens, wallet data,\nphone, email, and idempotency keys are never returned.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **id** (required): Id query parameter.\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Privacy-safe bid page\n- Envelope: `{ key, message, status, data? }` — branch on `key`, not only HTTP status.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "Other — Shared Orders",
      "id": "32ab474832ab474832ab4748",
      "description": "KamTeswa API — Other — Shared Orders",
      "item": [
        {
          "name": "List my orders or return requests (client or provider)",
          "id": "3ff75cea3ff75cea3ff75cea",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/orders?type=&status=&page=%7B%7Bpage%7D%7D&limit=%7B%7BperPage%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "orders"
              ],
              "variable": [],
              "query": [
                {
                  "key": "type",
                  "value": "",
                  "description": "Filter type as documented by the endpoint.\nRequired.\nType: string.\nAllowed values: order, return.",
                  "disabled": false
                },
                {
                  "key": "status",
                  "value": "",
                  "description": "Filter or status value as documented by the endpoint.\nRequired.\nType: string.\nAllowed values: new, current, finished, cancelled.",
                  "disabled": false
                },
                {
                  "key": "page",
                  "value": "{{page}}",
                  "description": "Page number for paginated lists (starts at 1).\nOptional.\nType: integer.\nExample variable: {{page}}",
                  "disabled": false
                },
                {
                  "key": "limit",
                  "value": "{{perPage}}",
                  "description": "Page size for paginated lists.\nOptional.\nType: integer.\nExample variable: {{perPage}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nList my orders or return requests (client or provider)\n\nShared list for **both** Client and Provider tokens.\n\n- `type=order`: normal orders scoped by the bearer token.\n  - Client → orders where the client is the buyer.\n  - Provider → orders where the provider is the seller (`clientName` exposed).\n  - `status` is **required**: `new` | `current` | `finished` | `cancelled`.\n  - `new` = awaiting provider decision / unpaid.\n  - `current` = paid / in progress (provider list also includes paid `new` rows).\n  - `finished` = client confirmed receipt.\n  - `cancelled` = cancelled or rejected orders.\n- `type=return`: return requests scoped by the same actor.\n  - `status` is **required**: `new` | `current` | `finished` only.\n  - Mapping: created → `new`, accepted/handed → `current`,\n    completed/rejected → `finished`.\n  - Response uses the same list card shape as normal orders.\n- Pagination uses `page` and `limit` query params.\n- Swagger UI: choose **Client token** or **Provider token** before Execute.\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **type** (required): Which list to load:\n- `order`: normal orders.\n- `return`: return requests for the same actor.\n- **status** (required): List tab filter.\n- `type=order`: `new` | `current` | `finished` | `cancelled`.\n- `type=return`: `new` | `current` | `finished` only\n  (`cancelled` is rejected).\n- **page** (optional): Page query parameter.\n  - Example: `{{page}}`\n- **limit** (optional): Limit query parameter.\n  - Example: `{{perPage}}` (the API query name remains `limit`)\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Orders list for the authenticated actor.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- May include `paginate`: `{ currentPage, lastPage, perPage, total }`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values.\n- Pagination uses query `page` + `limit`; the `limit` value is supplied by `{{perPage}}`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        },
        {
          "name": "Get order or return-request details",
          "id": "400047574000475740004757",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "lang",
                "value": "{{locale}}",
                "description": "Response language. Use `ar` or `en`. Bound to environment variable {{locale}} (default ar)."
              },
              {
                "key": "secretkey",
                "value": "{{secretKey}}",
                "description": "Platform secret key required by SecretKeyAuth. Use {{secretKey}} from the selected Postman environment. Header name is `secretkey` (OpenAPI SecretKeyAuth). Do not commit real production secrets. Note: `x-secret-key` is not accepted by this backend."
              },
              {
                "key": "Authorization",
                "value": "Bearer {{clientToken}}",
                "description": "Bearer token for the authenticated actor. Default sample uses {{clientToken}}. Switch to Bearer {{providerToken}} for provider flows. Filled after successful login."
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/order?type=&id=%7B%7BorderId%7D%7D",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "order"
              ],
              "variable": [],
              "query": [
                {
                  "key": "type",
                  "value": "",
                  "description": "Filter type as documented by the endpoint.\nRequired.\nType: string.\nAllowed values: order, return.",
                  "disabled": false
                },
                {
                  "key": "id",
                  "value": "{{orderId}}",
                  "description": "Resource identifier (MongoDB ObjectId).\nRequired.\nType: string.\nExample variable: {{orderId}}",
                  "disabled": false
                }
              ]
            },
            "description": "## Purpose\nGet order or return-request details\n\nShared details for **both** Client and Provider tokens.\n\n- `type=order`: a single owned order with product line, lifecycle\n  flags, and action buttons for the current actor.\n- `type=return`: a single owned return request (same OrderDetails DTO shape +\n  action flags remapped to return-request endpoints). `id` is the return-request ObjectId.\n- Ownership is enforced by the bearer token.\n- Swagger UI: choose **Client token** or **Provider token** before Execute.\n\n### Action buttons (`data.actions.*` — true only for the current actor's turn)\nSame keys for both `type=order` and `type=return`; endpoint target depends on `type`.\n\n| Button | type=order | type=return | When true |\n|---|---|---|---|\n| `actions.paymentButton` | `POST /order/payment` | always `false` | Client + accepted + unpaid |\n| `actions.cancelButton` | `PATCH /order/cancel` | always `false` | Client + awaiting approval |\n| `actions.receivedButton` | `PATCH /order/received` | `PATCH /return-request/received` | Order: client + paid + delivered_to_customer. Return: provider + client_delivered |\n| `actions.acceptButton` | `PATCH /order/accept` | `PATCH /return-request/accept` | Order: provider + awaiting approval. Return: provider + pending_review |\n| `actions.rejectButton` | `PATCH /order/reject` | `PATCH /return-request/reject` | Order: provider + unpaid NEW. Return: provider + pending_review |\n| `actions.deliveredToCustomerButton` | `PATCH /order/delivered` | `PATCH /return-request/delivered` | Order: provider + paid current + deliverable. Return: client + accepted |\n| `actions.returnRequestButton` | `POST /return-request` | always `false` | Client + finished received + no return yet |\n| `actions.chatButton` | chat room | chat room | Order/return not cancelled/rejected |\n| `actions.rateButton` | `POST /rate` | always `false` | Client + finished paid received + not rated |\n\n## Actor\nClient / Provider\n\n## Authorization\n- **SecretKeyAuth** — required on virtually every `/api` route.\n  - Header name in this API: `secretkey: {{secretKey}}`\n  - OpenAPI security scheme: `SecretKeyAuth`\n  - Note: some clients use the name `x-secret-key`; this backend reads `secretkey` only.\n- **ClientBearerAuth** or **ProviderBearerAuth** — required (exactly one actor).\n  - Default sample uses `Authorization: Bearer {{clientToken}}`\n  - Switch to `Bearer {{providerToken}}` when testing as provider.\n\n## Required Headers\n- `lang`: `{{locale}}` (`ar` | `en`)\n- `secretkey`: `{{secretKey}}` (SecretKeyAuth — set before running requests)\n- `Authorization`: `Bearer {{clientToken}}` or `Bearer {{providerToken}}`\n\n## Request Body\nNo request body for this endpoint.\n\n## Query Parameters\n- **type** (required): Which resource to load:\n- `order`: normal order details.\n- `return`: return-request details.\n- **id** (required): When `type=order`: Order ObjectId owned by the authenticated actor.\nWhen `type=return`: ReturnRequest ObjectId owned by the authenticated actor.\n  - Example: `665f1c2a9b4e1d0012ab34d0`\n\n## Path Parameters\nNo path parameters for this endpoint.\n\n## Successful Response\nStandard envelope:\n\n```json\n{\n  \"key\": \"success\",\n  \"message\": \"...\",\n  \"status\": 200,\n  \"data\": {}\n}\n```\n\nClients should branch on `key`, not only HTTP status.\n- Order details when `type=order`, or return-request details when `type=return`.\n- Envelope fields: `key`, `message`, `status`, and usually `data`.\n- Typical success `key`: `success`.\n- Notable `data` fields (from docs examples): `id`, `warningText`, `statusSteps`, `orderNumber`, `orderNumberText`, `date`, `time`, `reasonTitle`, `reason`, `status`, `statusText`, `product`.\n\n## Common Failure Cases\n- **Validation error** (`key: fail`, HTTP 400) — missing/invalid body, query, or path fields.\n- **Invalid secret key** — missing/wrong `secretkey` header or `{{secretKey}}` value.\n- **Unauthorized** (`key: unauthorized`, HTTP 419) — missing/expired/wrong bearer token.\n- **Blocked / needActive** — account blocked or not activated when the flow requires it.\n- **Exception** (`key: exception`, HTTP 500) — unexpected server error.\n\n## QA Notes\n- Set `{{secretKey}}` in the selected environment before calling this endpoint.\n- Obtain `{{clientToken}}` from **POST /signin** with `userType=client` when a client bearer is required.\n- Obtain `{{providerToken}}` from **POST /signin** with `userType=provider` when a provider bearer is required.\n- Confirm success and failure responses keep the `{ key, message, status }` envelope.\n- Confirm responses never include `password`, OTP, or `activationCode` values."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "let json = {};",
                  "try {",
                  "  json = pm.response.json();",
                  "} catch (error) {",
                  "  pm.test(\"Response is valid JSON\", function () {",
                  "    throw new Error(\"Response is not valid JSON\");",
                  "  });",
                  "}",
                  "",
                  "pm.test(\"Response has standard envelope\", function () {",
                  "  pm.expect(json).to.have.property(\"key\");",
                  "  pm.expect(json).to.have.property(\"message\");",
                  "  pm.expect(json).to.have.property(\"status\");",
                  "});",
                  "",
                  "pm.test(\"Envelope status is a number\", function () {",
                  "  pm.expect(json.status).to.be.a(\"number\");",
                  "});",
                  "",
                  "const raw = pm.response.text() || \"\";",
                  "pm.test(\"Response does not leak password fields\", function () {",
                  "  pm.expect(raw.includes('\"password\"')).to.eql(false);",
                  "});",
                  "",
                  "pm.test(\"Response does not return OTP/activationCode\", function () {",
                  "  pm.expect(raw.includes('\"activationCode\"')).to.eql(false);",
                  "  pm.expect(raw.includes('\"otp\"')).to.eql(false);",
                  "});",
                  ""
                ]
              }
            }
          ],
          "response": []
        }
      ]
    }
  ]
}
