{
  "info": {
    "name": "Jwab Business API v1 (preview)",
    "_postman_id": "6c1d9a52-7f0e-4c34-9a8e-1b2f0a7d4e91",
    "description": "# Jwab Business API — v1 (design preview)\n\nSend WhatsApp messages and Meta‑approved templates from your own systems — ERP, CRM, accounting, e‑commerce, POS — through the WhatsApp number you already connected in Jwab.\n\n> **Status:** design preview. Endpoints, fields and examples here show how the API **will** work. Names may change slightly before general availability; we will publish a changelog.\n\n## Typical use cases\n* Send an **invoice** or **quotation** PDF the moment it is issued in your ERP.\n* Payment reminders, order/shipping updates, appointment confirmations, OTP codes.\n* Sync customers into Jwab contacts so your team sees the full chat history in the Jwab inbox.\n* Receive **delivery/read receipts** and **customer replies** back in your system via webhooks.\n\n## Base URL\n| Environment | Base URL |\n|---|---|\n| Production | `https://api.jwab.ai/v1` |\n\nUse a **test key** (`jwab_test_…`) to try every endpoint without sending real WhatsApp messages: requests are validated exactly like live ones and return realistic responses, but nothing leaves Jwab and nothing counts against your quota.\n\n## Authentication\nCreate an API key in **Jwab → Settings → Developers → API keys** (workspace admins only). Send it as a bearer token:\n\n```\nAuthorization: Bearer jwab_live_8f3c…\n```\n\n* Keys belong to one workspace and can be restricted by **scope** (`messages:send`, `messages:read`, `templates:read`, `templates:write`, `contacts:write`, `media:write`, `webhooks:manage`) and optionally by **IP allow‑list**.\n* Keys are shown once. Rotate or revoke them at any time from the same page.\n* Never put a key in a browser or mobile app — call the API from your server.\n\n## Conventions\n* JSON in, JSON out, UTF‑8 (Arabic text is fully supported).\n* Phone numbers in **E.164** (`+201001234567`). We also accept `00201001234567` and `201001234567` and normalize them.\n* IDs are prefixed strings: `msg_`, `ch_`, `tpl_`, `ct_`, `med_`, `wh_`, `bat_`.\n* Timestamps are ISO‑8601 UTC.\n* **Idempotency:** send an `Idempotency-Key` header (any unique string, e.g. your invoice number + attempt) on every `POST`. A retry with the same key within 24 h returns the first result and never sends twice.\n* **`client_reference`** and **`metadata`**: attach your own IDs (invoice number, CRM deal id). They come back on every response and webhook so you can match results without storing our IDs.\n* **Attaching a PDF:** in `document`, send the file itself as base64 in `content` (up to 15 MB), a downloadable `url`, or a `media_id` from `POST /media`. Always include `filename`.\n* **Pagination:** list endpoints take `limit` (1–100, default 25) and `cursor`; responses contain `has_more` and `next_cursor`.\n\n## Sending rules (WhatsApp policy)\n* To start a conversation, or to message someone who has not written to you in the last **24 hours**, you must use an **approved template**.\n* Free‑form messages (text, documents, images) are allowed only **inside the 24‑hour window** after the customer’s last message. Outside it the API returns `409 outside_service_window`.\n* Templates are created in Jwab (or via `POST /templates`) and approved by Meta, usually within minutes to a few hours.\n* Contacts who opted out of marketing are blocked for `marketing` templates (`409 contact_opted_out`); `utility` and `authentication` templates still go through.\n\n## Message lifecycle\n`queued → sent → delivered → read`, or `failed` at any step. A `202 Accepted` response means Jwab queued the message; final status arrives by webhook or `GET /messages/{id}`.\n\n## Rate limits & quotas\n* API: 600 requests/min per key (`X-RateLimit-Limit`, `X-RateLimit-Remaining`, `Retry-After` on `429`).\n* WhatsApp throughput follows your number’s Meta messaging tier (1K/10K/100K/unlimited unique customers per 24 h).\n* **API messages are part of your Jwab subscription.** Each plan includes a monthly number of API messages; see `GET /usage`. When the quota runs out the API returns `402 quota_exceeded` unless overage is enabled for your workspace. Meta’s per‑message WhatsApp fees are billed separately as today.\n\n## Errors\nEvery error has the same shape:\n```json\n{ \"error\": { \"type\": \"invalid_request_error\", \"code\": \"template_not_approved\",\n  \"message\": \"Template 'invoice_ready' (ar) is PENDING at Meta.\", \"param\": \"template.name\",\n  \"request_id\": \"req_01JA8ZK3V9Q2\", \"doc_url\": \"https://docs.jwab.ai/api/errors#template_not_approved\" } }\n```\n| HTTP | Meaning |\n|---|---|\n| 400 | Invalid request (`validation_failed`, `invalid_phone_number`, `template_parameter_mismatch`, `invalid_document`, `document_unreachable`) |\n| 401 | Missing/invalid key (`unauthenticated`) |\n| 402 | Subscription inactive or quota used up (`subscription_inactive`, `quota_exceeded`) |\n| 403 | Key lacks the scope, or IP not allowed (`insufficient_scope`, `ip_not_allowed`) |\n| 404 | Not found |\n| 409 | Not allowed right now (`outside_service_window`, `contact_opted_out`, `template_not_approved`, `channel_disconnected`, `idempotency_conflict`) |\n| 422 | Rejected by WhatsApp (`whatsapp_rejected`, with Meta’s code in `error.meta`) |\n| 429 | Rate limited (`rate_limited`) |\n| 5xx | Our side — safe to retry with the same `Idempotency-Key` |\n\n## How to use this collection\n1. Import it into Postman, then import the **Jwab API — Test** environment.\n2. Put your test key in `api_key` and your WhatsApp channel in `channel_id` (from **Channels → List channels**).\n3. Run **Getting started → Who am I** to confirm the key works, then go through **Messages**.\n",
    "version": "1.0.0-preview",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{api_key}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://api.jwab.ai/v1"
    },
    {
      "key": "api_key",
      "value": "jwab_test_REPLACE_ME"
    },
    {
      "key": "channel_id",
      "value": "ch_01J9X4F2"
    },
    {
      "key": "test_phone",
      "value": "+201001234567"
    },
    {
      "key": "template_name",
      "value": "invoice_ready"
    },
    {
      "key": "message_id",
      "value": "msg_01JA8ZM4T6R1XK"
    },
    {
      "key": "batch_id",
      "value": "bat_01JA9B7E"
    },
    {
      "key": "media_id",
      "value": "med_01JA9C1F"
    },
    {
      "key": "webhook_id",
      "value": "wh_01JA9D2G"
    },
    {
      "key": "your_webhook_url",
      "value": "https://erp.acme.example/hooks/jwab"
    }
  ],
  "event": [
    {
      "listen": "test",
      "script": {
        "type": "text/javascript",
        "exec": [
          "// Save IDs from responses so the next requests can use them",
          "try {",
          "  const b = pm.response.json();",
          "  if (b && b.id) {",
          "    const map = {msg_: 'message_id', bat_: 'batch_id', med_: 'media_id', wh_: 'webhook_id'};",
          "    for (const p in map) if (String(b.id).startsWith(p)) pm.collectionVariables.set(map[p], b.id);",
          "  }",
          "} catch (e) {}"
        ]
      }
    }
  ],
  "item": [
    {
      "name": "0. Getting started",
      "description": "Check that your key works and see which workspace and scopes it belongs to.",
      "item": [
        {
          "name": "Who am I (verify API key)",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/me",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "me"
              ]
            },
            "description": "Returns the workspace, key and scopes for the key in `Authorization`. Use it as a health check from your ERP."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/me",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "me"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"object\": \"api_key\",\n  \"id\": \"key_01JA7P0Q\",\n  \"name\": \"Odoo production\",\n  \"mode\": \"live\",\n  \"scopes\": [\n    \"messages:send\",\n    \"messages:read\",\n    \"templates:read\",\n    \"media:write\",\n    \"webhooks:manage\"\n  ],\n  \"workspace\": {\n    \"id\": \"ws_44\",\n    \"name\": \"Acme Trading\",\n    \"timezone\": \"Africa/Cairo\"\n  },\n  \"created_at\": \"2026-10-01T08:00:00Z\",\n  \"last_used_at\": \"2026-10-13T09:14:55Z\"\n}"
            },
            {
              "name": "401 Invalid key",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/me",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "me"
                  ]
                }
              },
              "status": "Unauthorized",
              "code": 401,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"unauthenticated\",\n    \"message\": \"Invalid or revoked API key.\",\n    \"request_id\": \"req_01JA8ZK3V9Q2\",\n    \"doc_url\": \"https://docs.jwab.ai/api/errors#unauthenticated\"\n  }\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "1. Subscription & usage",
      "description": "Your API subscription and how much of this month's quota is used.",
      "item": [
        {
          "name": "Get usage for current period",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/usage",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "usage"
              ]
            },
            "description": "Plan, billing period and API message usage. `remaining` is `null` on unlimited plans."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/usage",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "usage"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"object\": \"usage\",\n  \"plan\": {\n    \"code\": \"business\",\n    \"name\": \"Business\",\n    \"api_access\": true\n  },\n  \"subscription_status\": \"active\",\n  \"period\": {\n    \"start\": \"2026-10-01T00:00:00Z\",\n    \"end\": \"2026-10-31T23:59:59Z\"\n  },\n  \"messages\": {\n    \"included\": 10000,\n    \"used\": 3420,\n    \"remaining\": 6580,\n    \"overage_enabled\": false,\n    \"by_category\": {\n      \"utility\": 3100,\n      \"marketing\": 250,\n      \"authentication\": 70,\n      \"service\": 0\n    }\n  },\n  \"api_requests_today\": 5822\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "2. Channels (senders)",
      "description": "WhatsApp numbers connected to your workspace. Every send needs a `channel_id`.",
      "item": [
        {
          "name": "List channels",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/channels?type=whatsapp",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "channels"
              ],
              "query": [
                {
                  "key": "type",
                  "value": "whatsapp",
                  "description": "whatsapp | messenger | instagram (only whatsapp supports templates today)",
                  "disabled": false
                }
              ]
            },
            "description": "All channels the key can send from, with WhatsApp quality rating and messaging tier."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/channels?type=whatsapp",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "channels"
                  ],
                  "query": [
                    {
                      "key": "type",
                      "value": "whatsapp",
                      "description": "whatsapp | messenger | instagram (only whatsapp supports templates today)",
                      "disabled": false
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"object\": \"list\",\n  \"has_more\": false,\n  \"next_cursor\": null,\n  \"data\": [\n    {\n      \"id\": \"ch_01J9X4F2\",\n      \"object\": \"channel\",\n      \"type\": \"whatsapp\",\n      \"name\": \"Acme Sales\",\n      \"phone_number\": \"+20225550100\",\n      \"status\": \"connected\",\n      \"quality_rating\": \"GREEN\",\n      \"messaging_limit_tier\": \"TIER_10K\",\n      \"default\": true\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Get channel",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/channels/{{channel_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "channels",
                "{{channel_id}}"
              ]
            },
            "description": "One channel by ID."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/channels/{{channel_id}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "channels",
                    "{{channel_id}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"ch_01J9X4F2\",\n  \"object\": \"channel\",\n  \"type\": \"whatsapp\",\n  \"name\": \"Acme Sales\",\n  \"phone_number\": \"+20225550100\",\n  \"status\": \"connected\",\n  \"quality_rating\": \"GREEN\",\n  \"messaging_limit_tier\": \"TIER_10K\",\n  \"default\": true\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "3. Templates",
      "description": "Meta‑approved message templates. Only `approved` templates can be sent. The `components` show exactly which parameters each send needs.",
      "item": [
        {
          "name": "List templates",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/templates?status=approved",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "templates"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "approved",
                  "description": "approved | pending | rejected | paused | disabled",
                  "disabled": false
                },
                {
                  "key": "category",
                  "value": "utility",
                  "description": "utility | marketing | authentication",
                  "disabled": true
                },
                {
                  "key": "language",
                  "value": "ar",
                  "description": "ar | en | …",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "",
                  "disabled": true
                }
              ]
            },
            "description": "Filter by status, category or language."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/templates?status=approved",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "templates"
                  ],
                  "query": [
                    {
                      "key": "status",
                      "value": "approved",
                      "description": "approved | pending | rejected | paused | disabled",
                      "disabled": false
                    },
                    {
                      "key": "category",
                      "value": "utility",
                      "description": "utility | marketing | authentication",
                      "disabled": true
                    },
                    {
                      "key": "language",
                      "value": "ar",
                      "description": "ar | en | …",
                      "disabled": true
                    },
                    {
                      "key": "limit",
                      "value": "25",
                      "description": "",
                      "disabled": true
                    },
                    {
                      "key": "cursor",
                      "value": "",
                      "description": "",
                      "disabled": true
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"object\": \"list\",\n  \"has_more\": false,\n  \"next_cursor\": null,\n  \"data\": [\n    {\n      \"id\": \"tpl_01J9Y0AB\",\n      \"object\": \"template\",\n      \"name\": \"invoice_ready\",\n      \"language\": \"ar\",\n      \"category\": \"utility\",\n      \"status\": \"approved\",\n      \"rejection_reason\": null,\n      \"components\": [\n        {\n          \"type\": \"header\",\n          \"format\": \"document\"\n        },\n        {\n          \"type\": \"body\",\n          \"text\": \"مرحباً {{1}}، فاتورتك رقم {{2}} بقيمة {{3}} جاهزة. تاريخ الاستحقاق {{4}}.\",\n          \"parameters\": [\n            {\n              \"index\": 1,\n              \"example\": \"أحمد\"\n            },\n            {\n              \"index\": 2,\n              \"example\": \"INV-2026-1042\"\n            },\n            {\n              \"index\": 3,\n              \"example\": \"1,250.00 EGP\"\n            },\n            {\n              \"index\": 4,\n              \"example\": \"2026-10-20\"\n            }\n          ]\n        },\n        {\n          \"type\": \"footer\",\n          \"text\": \"Acme Trading\"\n        },\n        {\n          \"type\": \"buttons\",\n          \"buttons\": [\n            {\n              \"index\": 0,\n              \"type\": \"url\",\n              \"text\": \"ادفع الآن\",\n              \"url\": \"https://pay.acme.example/{{1}}\",\n              \"example\": \"INV-2026-1042\"\n            }\n          ]\n        }\n      ],\n      \"channel_id\": \"ch_01J9X4F2\",\n      \"updated_at\": \"2026-10-02T11:20:00Z\"\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Get template",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/templates/{{template_name}}?language=ar",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "templates",
                "{{template_name}}"
              ],
              "query": [
                {
                  "key": "language",
                  "value": "ar",
                  "description": "",
                  "disabled": false
                }
              ]
            },
            "description": "Get by name (all languages) or by ID (`tpl_…`). Add `?language=ar` for one language."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/templates/{{template_name}}?language=ar",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "templates",
                    "{{template_name}}"
                  ],
                  "query": [
                    {
                      "key": "language",
                      "value": "ar",
                      "description": "",
                      "disabled": false
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"tpl_01J9Y0AB\",\n  \"object\": \"template\",\n  \"name\": \"invoice_ready\",\n  \"language\": \"ar\",\n  \"category\": \"utility\",\n  \"status\": \"approved\",\n  \"rejection_reason\": null,\n  \"components\": [\n    {\n      \"type\": \"header\",\n      \"format\": \"document\"\n    },\n    {\n      \"type\": \"body\",\n      \"text\": \"مرحباً {{1}}، فاتورتك رقم {{2}} بقيمة {{3}} جاهزة. تاريخ الاستحقاق {{4}}.\",\n      \"parameters\": [\n        {\n          \"index\": 1,\n          \"example\": \"أحمد\"\n        },\n        {\n          \"index\": 2,\n          \"example\": \"INV-2026-1042\"\n        },\n        {\n          \"index\": 3,\n          \"example\": \"1,250.00 EGP\"\n        },\n        {\n          \"index\": 4,\n          \"example\": \"2026-10-20\"\n        }\n      ]\n    },\n    {\n      \"type\": \"footer\",\n      \"text\": \"Acme Trading\"\n    },\n    {\n      \"type\": \"buttons\",\n      \"buttons\": [\n        {\n          \"index\": 0,\n          \"type\": \"url\",\n          \"text\": \"ادفع الآن\",\n          \"url\": \"https://pay.acme.example/{{1}}\",\n          \"example\": \"INV-2026-1042\"\n        }\n      ]\n    }\n  ],\n  \"channel_id\": \"ch_01J9X4F2\",\n  \"updated_at\": \"2026-10-02T11:20:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Create template (submit to Meta)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/templates",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "templates"
              ]
            },
            "description": "Creates a template and submits it to Meta for approval. Status starts `pending`; you get a `template.status_updated` webhook when Meta decides. Needs scope `templates:write`.\n\nUse `{{1}}`, `{{2}}` … for variables and give an example for each — Meta rejects templates without examples.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"quotation_ready\",\n  \"language\": \"en\",\n  \"category\": \"utility\",\n  \"components\": [\n    {\n      \"type\": \"header\",\n      \"format\": \"document\"\n    },\n    {\n      \"type\": \"body\",\n      \"text\": \"Hello {{1}}, your quotation {{2}} for {{3}} is attached. It is valid until {{4}}.\",\n      \"examples\": [\n        \"Sara\",\n        \"QT-2026-0311\",\n        \"USD 4,800.00\",\n        \"2026-11-12\"\n      ]\n    },\n    {\n      \"type\": \"footer\",\n      \"text\": \"Reply to this message to talk to our sales team\"\n    },\n    {\n      \"type\": \"buttons\",\n      \"buttons\": [\n        {\n          \"type\": \"url\",\n          \"text\": \"View quotation\",\n          \"url\": \"https://portal.acme.example/q/{{1}}\",\n          \"example\": \"QT-2026-0311\"\n        },\n        {\n          \"type\": \"quick_reply\",\n          \"text\": \"Accept\"\n        },\n        {\n          \"type\": \"quick_reply\",\n          \"text\": \"Request changes\"\n        }\n      ]\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "201 Created",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/templates",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "templates"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"quotation_ready\",\n  \"language\": \"en\",\n  \"category\": \"utility\",\n  \"components\": [\n    {\n      \"type\": \"header\",\n      \"format\": \"document\"\n    },\n    {\n      \"type\": \"body\",\n      \"text\": \"Hello {{1}}, your quotation {{2}} for {{3}} is attached. It is valid until {{4}}.\",\n      \"examples\": [\n        \"Sara\",\n        \"QT-2026-0311\",\n        \"USD 4,800.00\",\n        \"2026-11-12\"\n      ]\n    },\n    {\n      \"type\": \"footer\",\n      \"text\": \"Reply to this message to talk to our sales team\"\n    },\n    {\n      \"type\": \"buttons\",\n      \"buttons\": [\n        {\n          \"type\": \"url\",\n          \"text\": \"View quotation\",\n          \"url\": \"https://portal.acme.example/q/{{1}}\",\n          \"example\": \"QT-2026-0311\"\n        },\n        {\n          \"type\": \"quick_reply\",\n          \"text\": \"Accept\"\n        },\n        {\n          \"type\": \"quick_reply\",\n          \"text\": \"Request changes\"\n        }\n      ]\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"tpl_01JA90CD\",\n  \"object\": \"template\",\n  \"name\": \"quotation_ready\",\n  \"language\": \"en\",\n  \"category\": \"utility\",\n  \"status\": \"pending\",\n  \"created_at\": \"2026-10-13T09:30:00Z\"\n}"
            },
            {
              "name": "400 Missing example",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/templates",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "templates"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"quotation_ready\",\n  \"language\": \"en\",\n  \"category\": \"utility\",\n  \"components\": [\n    {\n      \"type\": \"header\",\n      \"format\": \"document\"\n    },\n    {\n      \"type\": \"body\",\n      \"text\": \"Hello {{1}}, your quotation {{2}} for {{3}} is attached. It is valid until {{4}}.\",\n      \"examples\": [\n        \"Sara\",\n        \"QT-2026-0311\",\n        \"USD 4,800.00\",\n        \"2026-11-12\"\n      ]\n    },\n    {\n      \"type\": \"footer\",\n      \"text\": \"Reply to this message to talk to our sales team\"\n    },\n    {\n      \"type\": \"buttons\",\n      \"buttons\": [\n        {\n          \"type\": \"url\",\n          \"text\": \"View quotation\",\n          \"url\": \"https://portal.acme.example/q/{{1}}\",\n          \"example\": \"QT-2026-0311\"\n        },\n        {\n          \"type\": \"quick_reply\",\n          \"text\": \"Accept\"\n        },\n        {\n          \"type\": \"quick_reply\",\n          \"text\": \"Request changes\"\n        }\n      ]\n    }\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"validation_failed\",\n    \"message\": \"Body has 4 variables but 3 examples.\",\n    \"request_id\": \"req_01JA8ZK3V9Q2\",\n    \"doc_url\": \"https://docs.jwab.ai/api/errors#validation_failed\",\n    \"param\": \"components[1].examples\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Delete template",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/templates/{{template_name}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "templates",
                "{{template_name}}"
              ]
            },
            "description": "Deletes the template (all languages) in Jwab and at Meta. Meta blocks re‑using the same name for 30 days."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "DELETE",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/templates/{{template_name}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "templates",
                    "{{template_name}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"tpl_01JA90CD\",\n  \"object\": \"template\",\n  \"deleted\": true\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "4. Messages",
      "description": "Send templates and session messages, check status, list history.\n\n**Fields on every send**\n* `channel_id` — optional if your workspace has a default channel.\n* `to` — customer phone, E.164.\n* `type` — `template`, `text`, `document`, `image`, `video`, `audio`, `location`, `interactive`.\n* `client_reference` (≤ 128 chars) and `metadata` (≤ 20 keys) — your own IDs, echoed back everywhere.\n* `contact` — optional; creates/updates the contact in Jwab (name, email, tags, custom fields).\n* `conversation` — optional; `assign_to_department` / `assign_to_user` so replies land with the right team, and `note` shown to agents.",
      "item": [
        {
          "name": "Send template — simple text (payment reminder)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "The smallest valid template send: body variables in order.\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"payment_reminder\",\n    \"language\": \"ar\",\n    \"body\": [\n      \"أحمد\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ]\n  },\n  \"client_reference\": \"INV-2026-1042\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"payment_reminder\",\n    \"language\": \"ar\",\n    \"body\": [\n      \"أحمد\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ]\n  },\n  \"client_reference\": \"INV-2026-1042\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"payment_reminder\",\n    \"language\": \"ar\",\n    \"category\": \"utility\"\n  },\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            },
            {
              "name": "400 Parameter mismatch",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"payment_reminder\",\n    \"language\": \"ar\",\n    \"body\": [\n      \"أحمد\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ]\n  },\n  \"client_reference\": \"INV-2026-1042\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"template_parameter_mismatch\",\n    \"message\": \"Template 'payment_reminder' (ar) expects 4 body parameters, got 3.\",\n    \"request_id\": \"req_01JA8ZK3V9Q2\",\n    \"doc_url\": \"https://docs.jwab.ai/api/errors#template_parameter_mismatch\",\n    \"param\": \"template.body\"\n  }\n}"
            },
            {
              "name": "409 Template not approved",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"payment_reminder\",\n    \"language\": \"ar\",\n    \"body\": [\n      \"أحمد\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ]\n  },\n  \"client_reference\": \"INV-2026-1042\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Conflict",
              "code": 409,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"template_not_approved\",\n    \"message\": \"Template 'payment_reminder' (ar) is PENDING at Meta.\",\n    \"request_id\": \"req_01JA8ZK3V9Q2\",\n    \"doc_url\": \"https://docs.jwab.ai/api/errors#template_not_approved\",\n    \"param\": \"template.name\"\n  }\n}"
            },
            {
              "name": "402 Quota exceeded",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"payment_reminder\",\n    \"language\": \"ar\",\n    \"body\": [\n      \"أحمد\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ]\n  },\n  \"client_reference\": \"INV-2026-1042\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Payment Required",
              "code": 402,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"quota_exceeded\",\n    \"message\": \"Your Business plan's 10,000 API messages for October are used. Upgrade or enable overage in Jwab → Billing.\",\n    \"request_id\": \"req_01JA8ZK3V9Q2\",\n    \"doc_url\": \"https://docs.jwab.ai/api/errors#quota_exceeded\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Send template — invoice PDF (file in the request)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "Sends an invoice PDF with an approved template in ONE call: put the file itself in `document.content` as base64. This is the easiest way for an ERP that creates the PDF in memory.\n\nThree ways to attach the PDF; use exactly one in `document`:\n* `content` — the file as base64, up to 15 MB. Nothing to host, one request.\n* `url` — an HTTPS link Jwab can download (a signed link valid for at least 1 hour is fine). See the next request.\n* `media_id` — upload first with **Media → Upload media**; best for files over 15 MB or one file sent to many customers.\n\nAlways send `filename`: it is the name the customer sees. WhatsApp accepts documents up to 100 MB.\n\n`buttons[].value` fills the dynamic part of a URL button (e.g. `https://pay.acme.example/{{1}}`).\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"header\": {\n      \"type\": \"document\",\n      \"document\": {\n        \"filename\": \"فاتورة INV-2026-1042.pdf\",\n        \"content\": \"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDM4Pj4Kc3RyZWFtCnicK+QyVDAAQXNDE…\"\n      }\n    },\n    \"body\": [\n      \"أحمد حسن\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ],\n    \"buttons\": [\n      {\n        \"index\": 0,\n        \"type\": \"url\",\n        \"value\": \"INV-2026-1042\"\n      }\n    ]\n  },\n  \"contact\": {\n    \"name\": \"Ahmed Hassan\",\n    \"email\": \"ahmed@example.com\",\n    \"tags\": [\n      \"customer\",\n      \"odoo\"\n    ],\n    \"custom_fields\": {\n      \"customer_code\": \"C-00981\"\n    }\n  },\n  \"conversation\": {\n    \"assign_to_department\": \"Accounts\",\n    \"note\": \"Invoice INV-2026-1042 sent from Odoo\"\n  },\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\",\n    \"amount\": \"1250.00\",\n    \"currency\": \"EGP\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"header\": {\n      \"type\": \"document\",\n      \"document\": {\n        \"filename\": \"فاتورة INV-2026-1042.pdf\",\n        \"content\": \"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDM4Pj4Kc3RyZWFtCnicK+QyVDAAQXNDE…\"\n      }\n    },\n    \"body\": [\n      \"أحمد حسن\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ],\n    \"buttons\": [\n      {\n        \"index\": 0,\n        \"type\": \"url\",\n        \"value\": \"INV-2026-1042\"\n      }\n    ]\n  },\n  \"contact\": {\n    \"name\": \"Ahmed Hassan\",\n    \"email\": \"ahmed@example.com\",\n    \"tags\": [\n      \"customer\",\n      \"odoo\"\n    ],\n    \"custom_fields\": {\n      \"customer_code\": \"C-00981\"\n    }\n  },\n  \"conversation\": {\n    \"assign_to_department\": \"Accounts\",\n    \"note\": \"Invoice INV-2026-1042 sent from Odoo\"\n  },\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\",\n    \"amount\": \"1250.00\",\n    \"currency\": \"EGP\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"category\": \"utility\"\n  },\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            },
            {
              "name": "400 Invalid document",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"header\": {\n      \"type\": \"document\",\n      \"document\": {\n        \"filename\": \"فاتورة INV-2026-1042.pdf\",\n        \"content\": \"JVBERi0xLjcKJcfsj6IKNSAwIG9iago8PC9GaWx0ZXIvRmxhdGVEZWNvZGUvTGVuZ3RoIDM4Pj4Kc3RyZWFtCnicK+QyVDAAQXNDE…\"\n      }\n    },\n    \"body\": [\n      \"أحمد حسن\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ],\n    \"buttons\": [\n      {\n        \"index\": 0,\n        \"type\": \"url\",\n        \"value\": \"INV-2026-1042\"\n      }\n    ]\n  },\n  \"contact\": {\n    \"name\": \"Ahmed Hassan\",\n    \"email\": \"ahmed@example.com\",\n    \"tags\": [\n      \"customer\",\n      \"odoo\"\n    ],\n    \"custom_fields\": {\n      \"customer_code\": \"C-00981\"\n    }\n  },\n  \"conversation\": {\n    \"assign_to_department\": \"Accounts\",\n    \"note\": \"Invoice INV-2026-1042 sent from Odoo\"\n  },\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\",\n    \"amount\": \"1250.00\",\n    \"currency\": \"EGP\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"invalid_document\",\n    \"message\": \"template.header.document.content is not a valid PDF (base64 could not be decoded, or the file is over 15 MB). For larger files upload them with POST /media and send media_id.\",\n    \"request_id\": \"req_01JA8ZK3V9Q2\",\n    \"doc_url\": \"https://docs.jwab.ai/api/errors#invalid_document\",\n    \"param\": \"template.header.document.content\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Send template — invoice PDF (link)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "Same as above, but Jwab downloads the PDF from `document.url`. Use this when your system already stores invoices online. The link must answer within 10 seconds with the file (no login page); a signed link valid for at least 1 hour is fine. Jwab downloads it once, when the message is queued.\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"header\": {\n      \"type\": \"document\",\n      \"document\": {\n        \"url\": \"https://erp.acme.example/files/INV-2026-1042.pdf?sig=abc123\",\n        \"filename\": \"فاتورة INV-2026-1042.pdf\"\n      }\n    },\n    \"body\": [\n      \"أحمد حسن\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ]\n  },\n  \"client_reference\": \"INV-2026-1042\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"header\": {\n      \"type\": \"document\",\n      \"document\": {\n        \"url\": \"https://erp.acme.example/files/INV-2026-1042.pdf?sig=abc123\",\n        \"filename\": \"فاتورة INV-2026-1042.pdf\"\n      }\n    },\n    \"body\": [\n      \"أحمد حسن\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ]\n  },\n  \"client_reference\": \"INV-2026-1042\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"category\": \"utility\"\n  },\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            },
            {
              "name": "400 Document unreachable",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"header\": {\n      \"type\": \"document\",\n      \"document\": {\n        \"url\": \"https://erp.acme.example/files/INV-2026-1042.pdf?sig=abc123\",\n        \"filename\": \"فاتورة INV-2026-1042.pdf\"\n      }\n    },\n    \"body\": [\n      \"أحمد حسن\",\n      \"INV-2026-1042\",\n      \"1,250.00 EGP\",\n      \"2026-10-20\"\n    ]\n  },\n  \"client_reference\": \"INV-2026-1042\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Bad Request",
              "code": 400,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"document_unreachable\",\n    \"message\": \"Could not download template.header.document.url: the server answered 403. Use a public or signed link, or send the file in content.\",\n    \"request_id\": \"req_01JA8ZK3V9Q2\",\n    \"doc_url\": \"https://docs.jwab.ai/api/errors#document_unreachable\",\n    \"param\": \"template.header.document.url\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Send template — quotation with named parameters",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "Templates created with **named** variables (`{{customer_name}}`) take an object instead of a list. Quick‑reply buttons (Accept / Request changes) come back as `message.received` webhooks with `button.payload`, so your CRM can update the deal automatically.\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+971501234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"quotation_ready_v2\",\n    \"language\": \"en\",\n    \"header\": {\n      \"type\": \"document\",\n      \"document\": {\n        \"media_id\": \"{{media_id}}\",\n        \"filename\": \"QT-2026-0311.pdf\"\n      }\n    },\n    \"body\": {\n      \"customer_name\": \"Sara\",\n      \"quote_no\": \"QT-2026-0311\",\n      \"total\": \"USD 4,800.00\",\n      \"valid_until\": \"12 Nov 2026\"\n    },\n    \"buttons\": [\n      {\n        \"index\": 0,\n        \"type\": \"url\",\n        \"value\": \"QT-2026-0311\"\n      },\n      {\n        \"index\": 1,\n        \"type\": \"quick_reply\",\n        \"payload\": \"quote_accept:QT-2026-0311\"\n      },\n      {\n        \"index\": 2,\n        \"type\": \"quick_reply\",\n        \"payload\": \"quote_changes:QT-2026-0311\"\n      }\n    ]\n  },\n  \"client_reference\": \"QT-2026-0311\",\n  \"metadata\": {\n    \"source\": \"hubspot\",\n    \"deal_id\": \"9876543210\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+971501234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"quotation_ready_v2\",\n    \"language\": \"en\",\n    \"header\": {\n      \"type\": \"document\",\n      \"document\": {\n        \"media_id\": \"{{media_id}}\",\n        \"filename\": \"QT-2026-0311.pdf\"\n      }\n    },\n    \"body\": {\n      \"customer_name\": \"Sara\",\n      \"quote_no\": \"QT-2026-0311\",\n      \"total\": \"USD 4,800.00\",\n      \"valid_until\": \"12 Nov 2026\"\n    },\n    \"buttons\": [\n      {\n        \"index\": 0,\n        \"type\": \"url\",\n        \"value\": \"QT-2026-0311\"\n      },\n      {\n        \"index\": 1,\n        \"type\": \"quick_reply\",\n        \"payload\": \"quote_accept:QT-2026-0311\"\n      },\n      {\n        \"index\": 2,\n        \"type\": \"quick_reply\",\n        \"payload\": \"quote_changes:QT-2026-0311\"\n      }\n    ]\n  },\n  \"client_reference\": \"QT-2026-0311\",\n  \"metadata\": {\n    \"source\": \"hubspot\",\n    \"deal_id\": \"9876543210\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+971501234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"quotation_ready_v2\",\n    \"language\": \"en\",\n    \"category\": \"utility\"\n  },\n  \"client_reference\": \"QT-2026-0311\",\n  \"metadata\": {\n    \"source\": \"hubspot\",\n    \"deal_id\": \"9876543210\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            }
          ]
        },
        {
          "name": "Send template — image header (order shipped)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "Image (`image`) and video (`video`) headers work the same way as documents.\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"order_shipped\",\n    \"language\": \"en\",\n    \"header\": {\n      \"type\": \"image\",\n      \"image\": {\n        \"url\": \"https://cdn.acme.example/orders/58231.jpg\"\n      }\n    },\n    \"body\": [\n      \"Mona\",\n      \"#58231\",\n      \"Aramex\",\n      \"AWB 3349981120\"\n    ],\n    \"buttons\": [\n      {\n        \"index\": 0,\n        \"type\": \"url\",\n        \"value\": \"3349981120\"\n      }\n    ]\n  },\n  \"client_reference\": \"SO-58231\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"order_shipped\",\n    \"language\": \"en\",\n    \"header\": {\n      \"type\": \"image\",\n      \"image\": {\n        \"url\": \"https://cdn.acme.example/orders/58231.jpg\"\n      }\n    },\n    \"body\": [\n      \"Mona\",\n      \"#58231\",\n      \"Aramex\",\n      \"AWB 3349981120\"\n    ],\n    \"buttons\": [\n      {\n        \"index\": 0,\n        \"type\": \"url\",\n        \"value\": \"3349981120\"\n      }\n    ]\n  },\n  \"client_reference\": \"SO-58231\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"category\": \"utility\"\n  },\n  \"client_reference\": \"SO-58231\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            }
          ]
        },
        {
          "name": "Send template — OTP (authentication)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "Authentication templates take the code once; Jwab fills the body and the copy‑code button. Set `ttl_seconds` so stale codes are dropped instead of delivered late.\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"login_code\",\n    \"language\": \"en\",\n    \"code\": \"482913\"\n  },\n  \"ttl_seconds\": 300,\n  \"client_reference\": \"login-77812\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"login_code\",\n    \"language\": \"en\",\n    \"code\": \"482913\"\n  },\n  \"ttl_seconds\": 300,\n  \"client_reference\": \"login-77812\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"login_code\",\n    \"language\": \"en\",\n    \"category\": \"authentication\"\n  },\n  \"client_reference\": \"login-77812\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            }
          ]
        },
        {
          "name": "Send text (inside 24h window)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "Free‑form text. Only allowed within 24 h of the customer's last message — otherwise `409 outside_service_window` (send a template instead). Set `preview_url` to show a link preview.\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"text\",\n  \"text\": {\n    \"body\": \"Thanks Ahmed, we received your payment for INV-2026-1042 ✅\",\n    \"preview_url\": false\n  },\n  \"client_reference\": \"PAY-55120\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"text\",\n  \"text\": {\n    \"body\": \"Thanks Ahmed, we received your payment for INV-2026-1042 ✅\",\n    \"preview_url\": false\n  },\n  \"client_reference\": \"PAY-55120\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"text\",\n  \"template\": null,\n  \"client_reference\": \"PAY-55120\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            },
            {
              "name": "409 Outside 24h window",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"text\",\n  \"text\": {\n    \"body\": \"Thanks Ahmed, we received your payment for INV-2026-1042 ✅\",\n    \"preview_url\": false\n  },\n  \"client_reference\": \"PAY-55120\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Conflict",
              "code": 409,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"error\": {\n    \"type\": \"invalid_request_error\",\n    \"code\": \"outside_service_window\",\n    \"message\": \"The customer's last message was 3 days ago. Use an approved template to start a new conversation.\",\n    \"request_id\": \"req_01JA8ZK3V9Q2\",\n    \"doc_url\": \"https://docs.jwab.ai/api/errors#outside_service_window\"\n  }\n}"
            }
          ]
        },
        {
          "name": "Send document (inside 24h window)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "Send a file without a template, e.g. when the customer asks for a statement in chat. Attach it the same three ways as a template document: `content` (base64), `url` or `media_id`. `caption` is optional.\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"document\",\n  \"document\": {\n    \"url\": \"https://erp.acme.example/files/statement-C-00981-2026-09.pdf\",\n    \"filename\": \"Statement Sep 2026.pdf\",\n    \"caption\": \"Your account statement for September\"\n  },\n  \"client_reference\": \"STMT-C-00981-202609\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"document\",\n  \"document\": {\n    \"url\": \"https://erp.acme.example/files/statement-C-00981-2026-09.pdf\",\n    \"filename\": \"Statement Sep 2026.pdf\",\n    \"caption\": \"Your account statement for September\"\n  },\n  \"client_reference\": \"STMT-C-00981-202609\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"document\",\n  \"template\": null,\n  \"client_reference\": \"STMT-C-00981-202609\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            }
          ]
        },
        {
          "name": "Send image (inside 24h window)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "JPEG/PNG up to 5 MB.\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"image\",\n  \"image\": {\n    \"media_id\": \"{{media_id}}\",\n    \"caption\": \"Product photo — SKU A-1190\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"image\",\n  \"image\": {\n    \"media_id\": \"{{media_id}}\",\n    \"caption\": \"Product photo — SKU A-1190\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"image\",\n  \"template\": null,\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            }
          ]
        },
        {
          "name": "Send interactive buttons (inside 24h window)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ]
            },
            "description": "Up to 3 reply buttons. The customer's tap arrives as `message.received` with `interactive.button_reply.id`.\n\nReturns `202 Accepted` with the queued message. Track the result by webhook (`message.sent/delivered/read/failed`) or `GET /messages/{id}`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"interactive\",\n  \"interactive\": {\n    \"type\": \"button\",\n    \"body\": {\n      \"text\": \"Would you like to confirm delivery for tomorrow 10:00–14:00?\"\n    },\n    \"buttons\": [\n      {\n        \"id\": \"deliv_ok:SO-58231\",\n        \"title\": \"Confirm\"\n      },\n      {\n        \"id\": \"deliv_resched:SO-58231\",\n        \"title\": \"Reschedule\"\n      }\n    ]\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"to\": \"{{test_phone}}\",\n  \"type\": \"interactive\",\n  \"interactive\": {\n    \"type\": \"button\",\n    \"body\": {\n      \"text\": \"Would you like to confirm delivery for tomorrow 10:00–14:00?\"\n    },\n    \"buttons\": [\n      {\n        \"id\": \"deliv_ok:SO-58231\",\n        \"title\": \"Confirm\"\n      },\n      {\n        \"id\": \"deliv_resched:SO-58231\",\n        \"title\": \"Reschedule\"\n      }\n    ]\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"queued\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"interactive\",\n  \"template\": null,\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\"\n}"
            }
          ]
        },
        {
          "name": "Get message",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages/{{message_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages",
                "{{message_id}}"
              ]
            },
            "description": "Current status, timestamps and failure reason."
          },
          "response": [
            {
              "name": "200 Read",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages/{{message_id}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages",
                    "{{message_id}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"read\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"category\": \"utility\"\n  },\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\",\n  \"wa_message_id\": \"wamid.HBgMMjAxMDAxMjM0NTY3FQIAERgSQjA2\",\n  \"pricing\": {\n    \"billable\": true,\n    \"category\": \"utility\"\n  },\n  \"timeline\": {\n    \"queued_at\": \"2026-10-13T09:15:02Z\",\n    \"sent_at\": \"2026-10-13T09:15:03Z\",\n    \"delivered_at\": \"2026-10-13T09:15:05Z\",\n    \"read_at\": \"2026-10-13T09:17:41Z\",\n    \"failed_at\": null\n  },\n  \"error\": null\n}"
            },
            {
              "name": "200 Failed",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages/{{message_id}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages",
                    "{{message_id}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"msg_01JA8ZM4T6R1XK\",\n  \"object\": \"message\",\n  \"status\": \"failed\",\n  \"channel_id\": \"{{channel_id}}\",\n  \"to\": \"+201001234567\",\n  \"type\": \"template\",\n  \"template\": {\n    \"name\": \"invoice_ready\",\n    \"language\": \"ar\",\n    \"category\": \"utility\"\n  },\n  \"client_reference\": \"INV-2026-1042\",\n  \"metadata\": {\n    \"source\": \"odoo\",\n    \"invoice_id\": \"1042\"\n  },\n  \"conversation_id\": \"conv_01JA8ZM4T7\",\n  \"contact_id\": \"ct_01JA7Q2B9X\",\n  \"created_at\": \"2026-10-13T09:15:02Z\",\n  \"wa_message_id\": \"wamid.HBgMMjAxMDAxMjM0NTY3FQIAERgSQjA2\",\n  \"pricing\": {\n    \"billable\": true,\n    \"category\": \"utility\"\n  },\n  \"timeline\": {\n    \"queued_at\": \"2026-10-13T09:15:02Z\",\n    \"sent_at\": \"2026-10-13T09:15:03Z\",\n    \"delivered_at\": null,\n    \"read_at\": null,\n    \"failed_at\": \"2026-10-13T09:15:04Z\"\n  },\n  \"error\": {\n    \"code\": \"recipient_not_on_whatsapp\",\n    \"message\": \"This phone number is not registered on WhatsApp.\",\n    \"meta\": {\n      \"code\": 131026\n    }\n  }\n}"
            }
          ]
        },
        {
          "name": "List messages",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages?client_reference=INV-2026-1042",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages"
              ],
              "query": [
                {
                  "key": "client_reference",
                  "value": "INV-2026-1042",
                  "description": "Exact match on your reference",
                  "disabled": false
                },
                {
                  "key": "status",
                  "value": "failed",
                  "description": "queued | sent | delivered | read | failed",
                  "disabled": true
                },
                {
                  "key": "to",
                  "value": "+201001234567",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "direction",
                  "value": "outbound",
                  "description": "outbound | inbound",
                  "disabled": true
                },
                {
                  "key": "created_after",
                  "value": "2026-10-01T00:00:00Z",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "",
                  "disabled": true
                }
              ]
            },
            "description": "Search outbound and inbound messages."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages?client_reference=INV-2026-1042",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages"
                  ],
                  "query": [
                    {
                      "key": "client_reference",
                      "value": "INV-2026-1042",
                      "description": "Exact match on your reference",
                      "disabled": false
                    },
                    {
                      "key": "status",
                      "value": "failed",
                      "description": "queued | sent | delivered | read | failed",
                      "disabled": true
                    },
                    {
                      "key": "to",
                      "value": "+201001234567",
                      "description": "",
                      "disabled": true
                    },
                    {
                      "key": "direction",
                      "value": "outbound",
                      "description": "outbound | inbound",
                      "disabled": true
                    },
                    {
                      "key": "created_after",
                      "value": "2026-10-01T00:00:00Z",
                      "description": "",
                      "disabled": true
                    },
                    {
                      "key": "limit",
                      "value": "25",
                      "description": "",
                      "disabled": true
                    },
                    {
                      "key": "cursor",
                      "value": "",
                      "description": "",
                      "disabled": true
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"object\": \"list\",\n  \"has_more\": false,\n  \"next_cursor\": null,\n  \"data\": [\n    {\n      \"id\": \"msg_01JA8ZM4T6R1XK\",\n      \"object\": \"message\",\n      \"status\": \"read\",\n      \"channel_id\": \"{{channel_id}}\",\n      \"to\": \"+201001234567\",\n      \"type\": \"template\",\n      \"template\": {\n        \"name\": \"invoice_ready\",\n        \"language\": \"ar\",\n        \"category\": \"utility\"\n      },\n      \"client_reference\": \"INV-2026-1042\",\n      \"metadata\": {\n        \"source\": \"odoo\",\n        \"invoice_id\": \"1042\"\n      },\n      \"conversation_id\": \"conv_01JA8ZM4T7\",\n      \"contact_id\": \"ct_01JA7Q2B9X\",\n      \"created_at\": \"2026-10-13T09:15:02Z\",\n      \"wa_message_id\": \"wamid.HBgMMjAxMDAxMjM0NTY3FQIAERgSQjA2\",\n      \"pricing\": {\n        \"billable\": true,\n        \"category\": \"utility\"\n      },\n      \"timeline\": {\n        \"queued_at\": \"2026-10-13T09:15:02Z\",\n        \"sent_at\": \"2026-10-13T09:15:03Z\",\n        \"delivered_at\": \"2026-10-13T09:15:05Z\",\n        \"read_at\": \"2026-10-13T09:17:41Z\",\n        \"failed_at\": null\n      },\n      \"error\": null\n    }\n  ]\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "5. Bulk sending (batches)",
      "description": "Send the same template to up to **1,000** recipients in one call — e.g. month‑end statements. Each recipient gets its own message and webhooks; per‑recipient errors don't fail the batch.",
      "item": [
        {
          "name": "Create batch",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages/batch",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages",
                "batch"
              ]
            },
            "description": "`defaults` apply to every item; each item overrides what differs. Sending is throttled to your Meta tier.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"defaults\": {\n    \"type\": \"template\",\n    \"template\": {\n      \"name\": \"statement_ready\",\n      \"language\": \"ar\"\n    }\n  },\n  \"messages\": [\n    {\n      \"to\": \"+201001234567\",\n      \"template\": {\n        \"header\": {\n          \"type\": \"document\",\n          \"document\": {\n            \"url\": \"https://erp.acme.example/st/C-00981.pdf\",\n            \"filename\": \"كشف حساب.pdf\"\n          }\n        },\n        \"body\": [\n          \"أحمد\",\n          \"سبتمبر 2026\",\n          \"3,400.00 EGP\"\n        ]\n      },\n      \"client_reference\": \"ST-C-00981-202609\"\n    },\n    {\n      \"to\": \"+201112223334\",\n      \"template\": {\n        \"header\": {\n          \"type\": \"document\",\n          \"document\": {\n            \"url\": \"https://erp.acme.example/st/C-01007.pdf\",\n            \"filename\": \"كشف حساب.pdf\"\n          }\n        },\n        \"body\": [\n          \"منى\",\n          \"سبتمبر 2026\",\n          \"0.00 EGP\"\n        ]\n      },\n      \"client_reference\": \"ST-C-01007-202609\"\n    }\n  ],\n  \"client_reference\": \"statements-2026-09\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "202 Accepted",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages/batch",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages",
                    "batch"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"channel_id\": \"{{channel_id}}\",\n  \"defaults\": {\n    \"type\": \"template\",\n    \"template\": {\n      \"name\": \"statement_ready\",\n      \"language\": \"ar\"\n    }\n  },\n  \"messages\": [\n    {\n      \"to\": \"+201001234567\",\n      \"template\": {\n        \"header\": {\n          \"type\": \"document\",\n          \"document\": {\n            \"url\": \"https://erp.acme.example/st/C-00981.pdf\",\n            \"filename\": \"كشف حساب.pdf\"\n          }\n        },\n        \"body\": [\n          \"أحمد\",\n          \"سبتمبر 2026\",\n          \"3,400.00 EGP\"\n        ]\n      },\n      \"client_reference\": \"ST-C-00981-202609\"\n    },\n    {\n      \"to\": \"+201112223334\",\n      \"template\": {\n        \"header\": {\n          \"type\": \"document\",\n          \"document\": {\n            \"url\": \"https://erp.acme.example/st/C-01007.pdf\",\n            \"filename\": \"كشف حساب.pdf\"\n          }\n        },\n        \"body\": [\n          \"منى\",\n          \"سبتمبر 2026\",\n          \"0.00 EGP\"\n        ]\n      },\n      \"client_reference\": \"ST-C-01007-202609\"\n    }\n  ],\n  \"client_reference\": \"statements-2026-09\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Accepted",
              "code": 202,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"bat_01JA9B7E\",\n  \"object\": \"batch\",\n  \"status\": \"processing\",\n  \"total\": 2,\n  \"accepted\": 2,\n  \"rejected\": 0,\n  \"rejections\": [],\n  \"client_reference\": \"statements-2026-09\",\n  \"created_at\": \"2026-10-13T10:00:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Get batch",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/messages/batch/{{batch_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "messages",
                "batch",
                "{{batch_id}}"
              ]
            },
            "description": "Progress counters. Use **List messages** with `batch_id` for per‑recipient detail."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/messages/batch/{{batch_id}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "messages",
                    "batch",
                    "{{batch_id}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"bat_01JA9B7E\",\n  \"object\": \"batch\",\n  \"status\": \"completed\",\n  \"total\": 2,\n  \"counts\": {\n    \"queued\": 0,\n    \"sent\": 0,\n    \"delivered\": 1,\n    \"read\": 1,\n    \"failed\": 0\n  },\n  \"client_reference\": \"statements-2026-09\",\n  \"completed_at\": \"2026-10-13T10:00:09Z\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "6. Media",
      "description": "Upload a file once and reuse its `media_id` in many sends (kept 30 days). Alternatively pass a `url` directly in the message.",
      "item": [
        {
          "name": "Upload media",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/media",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "media"
              ]
            },
            "description": "multipart/form-data. Documents up to 100 MB (PDF, DOCX, XLSX…), images 5 MB (JPEG/PNG), video 16 MB (MP4), audio 16 MB.",
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "file",
                  "type": "file",
                  "src": [],
                  "description": "The file to upload"
                },
                {
                  "key": "purpose",
                  "value": "whatsapp",
                  "type": "text"
                }
              ]
            }
          },
          "response": [
            {
              "name": "201 Created",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/media",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "media"
                  ]
                },
                "body": {
                  "mode": "formdata",
                  "formdata": [
                    {
                      "key": "file",
                      "type": "file",
                      "src": [],
                      "description": "The file to upload"
                    },
                    {
                      "key": "purpose",
                      "value": "whatsapp",
                      "type": "text"
                    }
                  ]
                }
              },
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"med_01JA9C1F\",\n  \"object\": \"media\",\n  \"filename\": \"QT-2026-0311.pdf\",\n  \"mime_type\": \"application/pdf\",\n  \"size\": 284113,\n  \"expires_at\": \"2026-11-12T09:40:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Get media",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/media/{{media_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "media",
                "{{media_id}}"
              ]
            },
            "description": "Metadata plus a short‑lived `download_url` (also used for files customers send you)."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/media/{{media_id}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "media",
                    "{{media_id}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"med_01JA9C1F\",\n  \"object\": \"media\",\n  \"filename\": \"QT-2026-0311.pdf\",\n  \"mime_type\": \"application/pdf\",\n  \"size\": 284113,\n  \"download_url\": \"https://files.jwab.ai/m/med_01JA9C1F?exp=1760350000&sig=…\",\n  \"expires_at\": \"2026-11-12T09:40:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Delete media",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/media/{{media_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "media",
                "{{media_id}}"
              ]
            },
            "description": ""
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "DELETE",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/media/{{media_id}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "media",
                    "{{media_id}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"med_01JA9C1F\",\n  \"object\": \"media\",\n  \"deleted\": true\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "7. Contacts",
      "description": "Keep Jwab contacts in sync with your CRM/ERP so agents see who they're talking to. `service_window_open_until` tells you whether a free‑form message is allowed right now.",
      "item": [
        {
          "name": "Create or update contact (upsert by phone)",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/contacts/{{test_phone}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "contacts",
                "{{test_phone}}"
              ]
            },
            "description": "Creates the contact if the phone is new, otherwise updates the fields you send. Tags are merged unless `replace_tags` is true.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Ahmed Hassan\",\n  \"email\": \"ahmed@example.com\",\n  \"tags\": [\n    \"customer\",\n    \"odoo\"\n  ],\n  \"custom_fields\": {\n    \"customer_code\": \"C-00981\",\n    \"city\": \"Cairo\"\n  },\n  \"language\": \"ar\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "PUT",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/contacts/{{test_phone}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "contacts",
                    "{{test_phone}}"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"name\": \"Ahmed Hassan\",\n  \"email\": \"ahmed@example.com\",\n  \"tags\": [\n    \"customer\",\n    \"odoo\"\n  ],\n  \"custom_fields\": {\n    \"customer_code\": \"C-00981\",\n    \"city\": \"Cairo\"\n  },\n  \"language\": \"ar\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"ct_01JA7Q2B9X\",\n  \"object\": \"contact\",\n  \"phone\": \"+201001234567\",\n  \"name\": \"Ahmed Hassan\",\n  \"email\": \"ahmed@example.com\",\n  \"tags\": [\n    \"customer\",\n    \"odoo\"\n  ],\n  \"custom_fields\": {\n    \"customer_code\": \"C-00981\"\n  },\n  \"marketing_opt_out\": false,\n  \"blocked\": false,\n  \"last_inbound_at\": \"2026-10-12T18:03:00Z\",\n  \"service_window_open_until\": \"2026-10-13T18:03:00Z\",\n  \"created_at\": \"2026-09-01T10:00:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Get contact",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/contacts/{{test_phone}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "contacts",
                "{{test_phone}}"
              ]
            },
            "description": "By phone (E.164) or by `ct_…` ID."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/contacts/{{test_phone}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "contacts",
                    "{{test_phone}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"ct_01JA7Q2B9X\",\n  \"object\": \"contact\",\n  \"phone\": \"+201001234567\",\n  \"name\": \"Ahmed Hassan\",\n  \"email\": \"ahmed@example.com\",\n  \"tags\": [\n    \"customer\",\n    \"odoo\"\n  ],\n  \"custom_fields\": {\n    \"customer_code\": \"C-00981\"\n  },\n  \"marketing_opt_out\": false,\n  \"blocked\": false,\n  \"last_inbound_at\": \"2026-10-12T18:03:00Z\",\n  \"service_window_open_until\": \"2026-10-13T18:03:00Z\",\n  \"created_at\": \"2026-09-01T10:00:00Z\"\n}"
            }
          ]
        },
        {
          "name": "List contacts",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/contacts?tag=odoo",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "contacts"
              ],
              "query": [
                {
                  "key": "tag",
                  "value": "odoo",
                  "description": "",
                  "disabled": false
                },
                {
                  "key": "updated_after",
                  "value": "2026-10-01T00:00:00Z",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "limit",
                  "value": "25",
                  "description": "",
                  "disabled": true
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "",
                  "disabled": true
                }
              ]
            },
            "description": ""
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/contacts?tag=odoo",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "contacts"
                  ],
                  "query": [
                    {
                      "key": "tag",
                      "value": "odoo",
                      "description": "",
                      "disabled": false
                    },
                    {
                      "key": "updated_after",
                      "value": "2026-10-01T00:00:00Z",
                      "description": "",
                      "disabled": true
                    },
                    {
                      "key": "limit",
                      "value": "25",
                      "description": "",
                      "disabled": true
                    },
                    {
                      "key": "cursor",
                      "value": "",
                      "description": "",
                      "disabled": true
                    }
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"object\": \"list\",\n  \"has_more\": false,\n  \"next_cursor\": null,\n  \"data\": [\n    {\n      \"id\": \"ct_01JA7Q2B9X\",\n      \"object\": \"contact\",\n      \"phone\": \"+201001234567\",\n      \"name\": \"Ahmed Hassan\",\n      \"email\": \"ahmed@example.com\",\n      \"tags\": [\n        \"customer\",\n        \"odoo\"\n      ],\n      \"custom_fields\": {\n        \"customer_code\": \"C-00981\"\n      },\n      \"marketing_opt_out\": false,\n      \"blocked\": false,\n      \"last_inbound_at\": \"2026-10-12T18:03:00Z\",\n      \"service_window_open_until\": \"2026-10-13T18:03:00Z\",\n      \"created_at\": \"2026-09-01T10:00:00Z\"\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Set marketing opt‑out",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/contacts/{{test_phone}}/opt-out",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "contacts",
                "{{test_phone}}",
                "opt-out"
              ]
            },
            "description": "Record that the customer doesn't want marketing messages (e.g. unsubscribed in your CRM). Use `DELETE` on the same path to opt back in.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"reason\": \"Unsubscribed via CRM\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/contacts/{{test_phone}}/opt-out",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "contacts",
                    "{{test_phone}}",
                    "opt-out"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"reason\": \"Unsubscribed via CRM\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"ct_01JA7Q2B9X\",\n  \"object\": \"contact\",\n  \"phone\": \"+201001234567\",\n  \"name\": \"Ahmed Hassan\",\n  \"email\": \"ahmed@example.com\",\n  \"tags\": [\n    \"customer\",\n    \"odoo\"\n  ],\n  \"custom_fields\": {\n    \"customer_code\": \"C-00981\"\n  },\n  \"marketing_opt_out\": true,\n  \"blocked\": false,\n  \"last_inbound_at\": \"2026-10-12T18:03:00Z\",\n  \"service_window_open_until\": \"2026-10-13T18:03:00Z\",\n  \"created_at\": \"2026-09-01T10:00:00Z\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "8. Webhooks",
      "description": "Get delivery receipts, customer replies and template approvals pushed to your system in real time. The **Event example** requests show the payloads Jwab will POST to *your* URL — you can point `your_webhook_url` at your dev server to test your handler.",
      "item": [
        {
          "name": "Create webhook endpoint",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks"
              ]
            },
            "description": "Register an HTTPS URL to receive events. The response contains the signing `secret` **once** — store it.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"https://erp.acme.example/hooks/jwab\",\n  \"description\": \"Odoo production\",\n  \"events\": [\n    \"message.sent\",\n    \"message.delivered\",\n    \"message.read\",\n    \"message.failed\",\n    \"message.received\",\n    \"template.status_updated\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "201 Created",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/webhooks",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "webhooks"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"url\": \"https://erp.acme.example/hooks/jwab\",\n  \"description\": \"Odoo production\",\n  \"events\": [\n    \"message.sent\",\n    \"message.delivered\",\n    \"message.read\",\n    \"message.failed\",\n    \"message.received\",\n    \"template.status_updated\"\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "Created",
              "code": 201,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"wh_01JA9D2G\",\n  \"object\": \"webhook_endpoint\",\n  \"url\": \"https://erp.acme.example/hooks/jwab\",\n  \"events\": [\n    \"message.sent\",\n    \"message.delivered\",\n    \"message.read\",\n    \"message.failed\",\n    \"message.received\",\n    \"template.status_updated\"\n  ],\n  \"status\": \"enabled\",\n  \"description\": \"Odoo production\",\n  \"created_at\": \"2026-10-13T09:50:00Z\",\n  \"secret\": \"whsec_9d7Hq2…\"\n}"
            }
          ]
        },
        {
          "name": "List webhook endpoints",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks"
              ]
            },
            "description": ""
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/webhooks",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "webhooks"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"object\": \"list\",\n  \"has_more\": false,\n  \"next_cursor\": null,\n  \"data\": [\n    {\n      \"id\": \"wh_01JA9D2G\",\n      \"object\": \"webhook_endpoint\",\n      \"url\": \"https://erp.acme.example/hooks/jwab\",\n      \"events\": [\n        \"message.sent\",\n        \"message.delivered\",\n        \"message.read\",\n        \"message.failed\",\n        \"message.received\",\n        \"template.status_updated\"\n      ],\n      \"status\": \"enabled\",\n      \"description\": \"Odoo production\",\n      \"created_at\": \"2026-10-13T09:50:00Z\"\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Update webhook endpoint",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks/{{webhook_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks",
                "{{webhook_id}}"
              ]
            },
            "description": "Change URL, events, or set `status` to `disabled`.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"events\": [\n    \"message.delivered\",\n    \"message.read\",\n    \"message.failed\",\n    \"message.received\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "PATCH",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/webhooks/{{webhook_id}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "webhooks",
                    "{{webhook_id}}"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"events\": [\n    \"message.delivered\",\n    \"message.read\",\n    \"message.failed\",\n    \"message.received\"\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"wh_01JA9D2G\",\n  \"object\": \"webhook_endpoint\",\n  \"url\": \"https://erp.acme.example/hooks/jwab\",\n  \"events\": [\n    \"message.delivered\",\n    \"message.read\",\n    \"message.failed\",\n    \"message.received\"\n  ],\n  \"status\": \"enabled\",\n  \"description\": \"Odoo production\",\n  \"created_at\": \"2026-10-13T09:50:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Send test event",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks/{{webhook_id}}/test",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks",
                "{{webhook_id}}",
                "test"
              ]
            },
            "description": "Sends a signed `webhook.test` event to the endpoint and returns your server's response.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"event\": \"message.delivered\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Content-Type",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/webhooks/{{webhook_id}}/test",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "webhooks",
                    "{{webhook_id}}",
                    "test"
                  ]
                },
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"event\": \"message.delivered\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"delivered\": true,\n  \"response_status\": 200,\n  \"duration_ms\": 184\n}"
            }
          ]
        },
        {
          "name": "Rotate signing secret",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              },
              {
                "key": "Idempotency-Key",
                "value": "{{$guid}}",
                "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks/{{webhook_id}}/rotate-secret",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks",
                "{{webhook_id}}",
                "rotate-secret"
              ]
            },
            "description": "Issues a new secret. The old one keeps working for 24 h so you can deploy without dropping events."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "POST",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  },
                  {
                    "key": "Idempotency-Key",
                    "value": "{{$guid}}",
                    "description": "Optional but recommended. Retrying with the same key within 24h returns the original result instead of sending twice."
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/webhooks/{{webhook_id}}/rotate-secret",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "webhooks",
                    "{{webhook_id}}",
                    "rotate-secret"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"wh_01JA9D2G\",\n  \"secret\": \"whsec_X1b0…\",\n  \"previous_secret_expires_at\": \"2026-10-14T10:00:00Z\"\n}"
            }
          ]
        },
        {
          "name": "Delete webhook endpoint",
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Accept",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/webhooks/{{webhook_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "webhooks",
                "{{webhook_id}}"
              ]
            },
            "description": ""
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "DELETE",
                "header": [
                  {
                    "key": "Accept",
                    "value": "application/json"
                  }
                ],
                "url": {
                  "raw": "{{base_url}}/webhooks/{{webhook_id}}",
                  "host": [
                    "{{base_url}}"
                  ],
                  "path": [
                    "webhooks",
                    "{{webhook_id}}"
                  ]
                }
              },
              "status": "OK",
              "code": 200,
              "_postman_previewlanguage": "json",
              "header": [
                {
                  "key": "Content-Type",
                  "value": "application/json"
                },
                {
                  "key": "X-Request-Id",
                  "value": "req_01JA8ZK3V9Q2"
                },
                {
                  "key": "X-RateLimit-Limit",
                  "value": "600"
                },
                {
                  "key": "X-RateLimit-Remaining",
                  "value": "598"
                }
              ],
              "body": "{\n  \"id\": \"wh_01JA9D2G\",\n  \"object\": \"webhook_endpoint\",\n  \"deleted\": true\n}"
            }
          ]
        },
        {
          "name": "Event example — message.delivered",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Jwab-Event",
                "value": "message.delivered"
              },
              {
                "key": "Jwab-Signature",
                "value": "t=1760346905,v1=<hmac>"
              }
            ],
            "url": {
              "raw": "{{your_webhook_url}}"
            },
            "description": "**Example payload Jwab sends to your URL** (not an endpoint you call).\n\nHeaders:\n```\nContent-Type: application/json\nJwab-Event: message.delivered\nJwab-Delivery: dlv_01JA9F…\nJwab-Signature: t=1760346905,v1=5f2b…\n```\nVerify: `v1 == HMAC_SHA256(secret, t + \".\" + raw_body)` and reject if `t` is older than 5 minutes. Reply `2xx` within 10 s; we retry with backoff for 24 h, and the same `event.id` may arrive twice — dedupe on it.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"evt_01JA9EMESS\",\n  \"object\": \"event\",\n  \"type\": \"message.delivered\",\n  \"created_at\": \"2026-10-13T09:15:05Z\",\n  \"workspace_id\": \"ws_44\",\n  \"livemode\": true,\n  \"data\": {\n    \"message\": {\n      \"id\": \"msg_01JA8ZM4T6R1XK\",\n      \"status\": \"delivered\",\n      \"to\": \"+201001234567\",\n      \"client_reference\": \"INV-2026-1042\",\n      \"metadata\": {\n        \"source\": \"odoo\",\n        \"invoice_id\": \"1042\"\n      },\n      \"delivered_at\": \"2026-10-13T09:15:05Z\"\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Event example — message.read",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Jwab-Event",
                "value": "message.read"
              },
              {
                "key": "Jwab-Signature",
                "value": "t=1760346905,v1=<hmac>"
              }
            ],
            "url": {
              "raw": "{{your_webhook_url}}"
            },
            "description": "**Example payload Jwab sends to your URL** (not an endpoint you call).\n\nHeaders:\n```\nContent-Type: application/json\nJwab-Event: message.read\nJwab-Delivery: dlv_01JA9F…\nJwab-Signature: t=1760346905,v1=5f2b…\n```\nVerify: `v1 == HMAC_SHA256(secret, t + \".\" + raw_body)` and reject if `t` is older than 5 minutes. Reply `2xx` within 10 s; we retry with backoff for 24 h, and the same `event.id` may arrive twice — dedupe on it.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"evt_01JA9EMESS\",\n  \"object\": \"event\",\n  \"type\": \"message.read\",\n  \"created_at\": \"2026-10-13T09:15:05Z\",\n  \"workspace_id\": \"ws_44\",\n  \"livemode\": true,\n  \"data\": {\n    \"message\": {\n      \"id\": \"msg_01JA8ZM4T6R1XK\",\n      \"status\": \"read\",\n      \"to\": \"+201001234567\",\n      \"client_reference\": \"INV-2026-1042\",\n      \"read_at\": \"2026-10-13T09:17:41Z\"\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Event example — message.failed",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Jwab-Event",
                "value": "message.failed"
              },
              {
                "key": "Jwab-Signature",
                "value": "t=1760346905,v1=<hmac>"
              }
            ],
            "url": {
              "raw": "{{your_webhook_url}}"
            },
            "description": "**Example payload Jwab sends to your URL** (not an endpoint you call).\n\nHeaders:\n```\nContent-Type: application/json\nJwab-Event: message.failed\nJwab-Delivery: dlv_01JA9F…\nJwab-Signature: t=1760346905,v1=5f2b…\n```\nVerify: `v1 == HMAC_SHA256(secret, t + \".\" + raw_body)` and reject if `t` is older than 5 minutes. Reply `2xx` within 10 s; we retry with backoff for 24 h, and the same `event.id` may arrive twice — dedupe on it.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"evt_01JA9EMESS\",\n  \"object\": \"event\",\n  \"type\": \"message.failed\",\n  \"created_at\": \"2026-10-13T09:15:05Z\",\n  \"workspace_id\": \"ws_44\",\n  \"livemode\": true,\n  \"data\": {\n    \"message\": {\n      \"id\": \"msg_01JA8ZM4T6R1XK\",\n      \"status\": \"failed\",\n      \"to\": \"+201001234567\",\n      \"client_reference\": \"INV-2026-1042\",\n      \"error\": {\n        \"code\": \"recipient_not_on_whatsapp\",\n        \"message\": \"This phone number is not registered on WhatsApp.\",\n        \"meta\": {\n          \"code\": 131026\n        }\n      }\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Event example — message.received (customer reply)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Jwab-Event",
                "value": "message.received"
              },
              {
                "key": "Jwab-Signature",
                "value": "t=1760346905,v1=<hmac>"
              }
            ],
            "url": {
              "raw": "{{your_webhook_url}}"
            },
            "description": "**Example payload Jwab sends to your URL** (not an endpoint you call).\n\nHeaders:\n```\nContent-Type: application/json\nJwab-Event: message.received\nJwab-Delivery: dlv_01JA9F…\nJwab-Signature: t=1760346905,v1=5f2b…\n```\nVerify: `v1 == HMAC_SHA256(secret, t + \".\" + raw_body)` and reject if `t` is older than 5 minutes. Reply `2xx` within 10 s; we retry with backoff for 24 h, and the same `event.id` may arrive twice — dedupe on it.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"evt_01JA9EMESS\",\n  \"object\": \"event\",\n  \"type\": \"message.received\",\n  \"created_at\": \"2026-10-13T09:15:05Z\",\n  \"workspace_id\": \"ws_44\",\n  \"livemode\": true,\n  \"data\": {\n    \"message\": {\n      \"id\": \"msg_01JA9G0H\",\n      \"direction\": \"inbound\",\n      \"from\": \"+201001234567\",\n      \"channel_id\": \"ch_01J9X4F2\",\n      \"type\": \"text\",\n      \"text\": {\n        \"body\": \"تم التحويل، شكراً\"\n      },\n      \"context\": {\n        \"reply_to_message_id\": \"msg_01JA8ZM4T6R1XK\",\n        \"client_reference\": \"INV-2026-1042\"\n      },\n      \"contact\": {\n        \"id\": \"ct_01JA7Q2B9X\",\n        \"name\": \"Ahmed Hassan\"\n      },\n      \"conversation_id\": \"conv_01JA8ZM4T7\",\n      \"received_at\": \"2026-10-13T09:20:12Z\"\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Event example — message.received (quotation button tap)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Jwab-Event",
                "value": "message.received"
              },
              {
                "key": "Jwab-Signature",
                "value": "t=1760346905,v1=<hmac>"
              }
            ],
            "url": {
              "raw": "{{your_webhook_url}}"
            },
            "description": "**Example payload Jwab sends to your URL** (not an endpoint you call).\n\nHeaders:\n```\nContent-Type: application/json\nJwab-Event: message.received\nJwab-Delivery: dlv_01JA9F…\nJwab-Signature: t=1760346905,v1=5f2b…\n```\nVerify: `v1 == HMAC_SHA256(secret, t + \".\" + raw_body)` and reject if `t` is older than 5 minutes. Reply `2xx` within 10 s; we retry with backoff for 24 h, and the same `event.id` may arrive twice — dedupe on it.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"evt_01JA9EMESS\",\n  \"object\": \"event\",\n  \"type\": \"message.received\",\n  \"created_at\": \"2026-10-13T09:15:05Z\",\n  \"workspace_id\": \"ws_44\",\n  \"livemode\": true,\n  \"data\": {\n    \"message\": {\n      \"id\": \"msg_01JA9H1J\",\n      \"direction\": \"inbound\",\n      \"from\": \"+971501234567\",\n      \"type\": \"button\",\n      \"button\": {\n        \"text\": \"Accept\",\n        \"payload\": \"quote_accept:QT-2026-0311\"\n      },\n      \"context\": {\n        \"reply_to_message_id\": \"msg_01JA8ZQ0\",\n        \"client_reference\": \"QT-2026-0311\"\n      },\n      \"received_at\": \"2026-10-13T11:02:40Z\"\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Event example — template.status_updated",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "Jwab-Event",
                "value": "template.status_updated"
              },
              {
                "key": "Jwab-Signature",
                "value": "t=1760346905,v1=<hmac>"
              }
            ],
            "url": {
              "raw": "{{your_webhook_url}}"
            },
            "description": "**Example payload Jwab sends to your URL** (not an endpoint you call).\n\nHeaders:\n```\nContent-Type: application/json\nJwab-Event: template.status_updated\nJwab-Delivery: dlv_01JA9F…\nJwab-Signature: t=1760346905,v1=5f2b…\n```\nVerify: `v1 == HMAC_SHA256(secret, t + \".\" + raw_body)` and reject if `t` is older than 5 minutes. Reply `2xx` within 10 s; we retry with backoff for 24 h, and the same `event.id` may arrive twice — dedupe on it.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"evt_01JA9ETEMP\",\n  \"object\": \"event\",\n  \"type\": \"template.status_updated\",\n  \"created_at\": \"2026-10-13T09:15:05Z\",\n  \"workspace_id\": \"ws_44\",\n  \"livemode\": true,\n  \"data\": {\n    \"template\": {\n      \"id\": \"tpl_01JA90CD\",\n      \"name\": \"quotation_ready\",\n      \"language\": \"en\",\n      \"status\": \"approved\",\n      \"previous_status\": \"pending\",\n      \"rejection_reason\": null\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        }
      ]
    }
  ]
}