{
 "info": {
  "_postman_id": "d9aca670-bd8c-586f-8d73-9524ec01b54f",
  "name": "Wazzapo API v2",
  "description": "Коллекция собрана из https://wazzapo.kz/api/v2/external/openapi.json. Вставьте ключ в переменную apiKey: ключ wz_test_… ничего не отправляет и не меняет.",
  "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
 },
 "auth": {
  "type": "apikey",
  "apikey": [
   {
    "key": "key",
    "value": "X-API-Key",
    "type": "string"
   },
   {
    "key": "value",
    "value": "{{apiKey}}",
    "type": "string"
   },
   {
    "key": "in",
    "value": "header",
    "type": "string"
   }
  ]
 },
 "variable": [
  {
   "key": "baseUrl",
   "value": "https://wazzapo.kz"
  },
  {
   "key": "apiKey",
   "value": "wz_test_вставьте_ключ"
  }
 ],
 "item": [
  {
   "id": "5e1814df-d583-5ba4-9750-9f886b71d8cb",
   "name": "contacts",
   "item": [
    {
     "id": "9bd36ef4-3cd7-5c29-b3a0-adc268e7ef6b",
     "name": "Создать контакт",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/contacts",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "contacts"
       ],
       "variable": [],
       "query": []
      },
      "description": "Создать контакт\n\nДобавляет клиента в контакты: номер обязателен, имя, почта, метки, свои поля и external_id по желанию.\n\nНомер приводится к виду 77071234567: плюс можно, 8 в начале заменяется на 7. Удалённый ранее\nконтакт с этим номером восстанавливается с новыми данными. Свои поля сначала создайте в кабинете,\nраздел «Поля контакта». Заголовок Idempotency-Key защищает от двойного создания при повторе запроса.\nОшибки: 409 contact_exists (номер уже есть: id в error.details.contact_id, измените его через\nPATCH /contacts/{contact_id}), 422 invalid_phone, 422 unknown_custom_field, 403 USAGE_LIMIT_EXCEEDED\n(лимит контактов тарифа).",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"custom_fields\": {\n    \"city\": \"Алматы\",\n    \"orders_count\": 3\n  },\n  \"email\": \"aigul@example.kz\",\n  \"external_id\": \"1c-00042\",\n  \"name\": \"Айгуль Серикова\",\n  \"phone\": \"77071234567\",\n  \"tags\": [\n    \"vip\",\n    \"almaty\"\n  ]\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "219a3336-2aeb-5fcd-9fb7-531789e90e54",
     "name": "Удалить контакт",
     "request": {
      "method": "DELETE",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/contacts/:contact_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "contacts",
        ":contact_id"
       ],
       "variable": [
        {
         "key": "contact_id",
         "value": "<contact_id>",
         "description": "id контакта: поле id из GET /contacts или из ответа POST /contacts, например 6712f0c4a1b2c3d4e5f60718"
        }
       ],
       "query": []
      },
      "description": "Удалить контакт\n\nУдаляет контакт мягко, как в кабинете: он пропадает из списка и поиска, переписка остаётся.\n\nВосстановить: клиент напишет с этого номера или вызовите POST /contacts с тем же номером.\nОтвет 204 без тела. Ошибки: 404 contact_not_found (уже удалён или чужой)."
     }
    },
    {
     "id": "d157b315-f0ab-52c5-aed3-878be8899b89",
     "name": "Получить контакт",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/contacts/:contact_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "contacts",
        ":contact_id"
       ],
       "variable": [
        {
         "key": "contact_id",
         "value": "<contact_id>",
         "description": "id контакта: поле id из GET /contacts или из ответа POST /contacts, например 6712f0c4a1b2c3d4e5f60718"
        }
       ],
       "query": []
      },
      "description": "Получить контакт\n\nКарточка одного контакта: номер, имя, метки, свои поля, источник (attribution).\n\nid берите из GET /contacts (найти по номеру: ?search=77071234567) или из ответа создания.\nОшибки: 404 contact_not_found, если контакта нет в этом аккаунте или он удалён."
     }
    },
    {
     "id": "77a8ea5d-6346-5749-be63-7d4ba5520a42",
     "name": "Список контактов",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/contacts",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "contacts"
       ],
       "variable": [],
       "query": [
        {
         "key": "search",
         "value": "<search>",
         "description": "Поиск без учёта регистра по части имени (Айгуль) или номера от 3 цифр (7071234, 8 707 123 45 67, +7 707 123-45-67)",
         "disabled": true
        },
        {
         "key": "tag",
         "value": "<tag>",
         "description": "Только контакты с этой меткой, например vip",
         "disabled": true
        },
        {
         "key": "updated_since",
         "value": "<updated_since>",
         "description": "Только изменённые с этого времени, ISO 8601: 2026-10-01T00:00:00Z. Удобно для обмена с вашей CRM: запоминайте время прошлого запроса",
         "disabled": true
        },
        {
         "key": "external_id",
         "value": "<external_id>",
         "description": "Только контакты с этим external_id (вашим id из своей CRM), например 1c-00042",
         "disabled": true
        },
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сколько контактов на странице, от 1 до 200",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; без него первая страница",
         "disabled": true
        }
       ]
      },
      "description": "Список контактов\n\nКонтакты аккаунта постранично, старые первыми. Удалённые и объединённые не показываются.\n\nФильтры складываются: ?search=Айгуль&tag=vip. Найти клиента своей CRM: ?external_id=1c-00042.\nСледующая страница: передайте next_cursor из ответа в ?cursor=, пока has_more=true.\nОшибки: 401 неверный ключ, 422 неверный cursor или updated_since."
     }
    },
    {
     "id": "b598368d-97f6-547a-87aa-3306ca662e72",
     "name": "Изменить контакт",
     "request": {
      "method": "PATCH",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/contacts/:contact_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "contacts",
        ":contact_id"
       ],
       "variable": [
        {
         "key": "contact_id",
         "value": "<contact_id>",
         "description": "id контакта: поле id из GET /contacts или из ответа POST /contacts, например 6712f0c4a1b2c3d4e5f60718"
        }
       ],
       "query": []
      },
      "description": "Изменить контакт\n\nМеняет только переданные поля, остальные остаются как были. Номер не меняется.\n\ntags заменяют весь список меток. custom_fields дополняют сохранённые: переданный ключ\nперезаписывается, null очищает поле, непереданные не трогаются. Изменение видно менеджерам\nсразу и запускает сценарии на изменение полей, как правка в кабинете.\nОшибки: 404 contact_not_found, 422 unknown_custom_field.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"custom_fields\": {\n    \"orders_count\": 4\n  },\n  \"name\": \"Айгуль Серикова\",\n  \"tags\": [\n    \"vip\",\n    \"almaty\",\n    \"implant\"\n  ]\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    }
   ]
  },
  {
   "id": "5a0d85cf-0a76-5542-aa5d-fd463967157c",
   "name": "conversations",
   "item": [
    {
     "id": "235e0cdb-8718-5090-bb02-d434c5f9b988",
     "name": "Добавить заметку",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/conversations/:conversation_id/notes",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "conversations",
        ":conversation_id",
        "notes"
       ],
       "variable": [
        {
         "key": "conversation_id",
         "value": "<conversation_id>",
         "description": "id диалога: поле id из GET /api/v2/external/conversations или conversation_id из вебхука message.incoming, например 6a7d5d3712f8e1a5c19f40c4"
        }
       ],
       "query": []
      },
      "description": "Добавить заметку\n\nДобавляет заметку сотрудника в диалог: её видят только сотрудники в кабинете, клиенту она не уходит. Упомянутым в `mention_user_ids` (id из GET /team) приходит уведомление в кабинете. Автор заметки сотрудник, чей ключ. Уходит вебхук `conversation.note_added` (только по явной подписке).\n\nПередайте заголовок `Idempotency-Key`: повтор запроса с тем же ключом не создаст вторую заметку. В переписке заметка видна с `include_notes=true`.\n\nОшибки: 403 `FORBIDDEN`, 404 `conversation_not_found`, 422 `VALIDATION_ERROR` (пустой текст или id не из GET /team). Право ключа: `conversations:write`.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"mention_user_ids\": [\n    \"7\"\n  ],\n  \"text\": \"@Ерлан клиентка просит счёт на ТОО, сумма 45 000 ₸\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "fbd174a7-06cf-5d9f-9dc9-6e66a9e83419",
     "name": "Назначить ответственного",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/conversations/:conversation_id/assign",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "conversations",
        ":conversation_id",
        "assign"
       ],
       "variable": [
        {
         "key": "conversation_id",
         "value": "<conversation_id>",
         "description": "id диалога: поле id из GET /api/v2/external/conversations или conversation_id из вебхука message.incoming, например 6a7d5d3712f8e1a5c19f40c4"
        }
       ],
       "query": []
      },
      "description": "Назначить ответственного\n\nНазначает ответственного менеджера (`user_id`) или снимает его (`null`). id сотрудника возьмите из GET /team: поле `id`, сотрудник должен быть активным (`is_active: true`) и не VIEWER.\n\nПрава как в чате: назначить на другого может владелец, администратор команды или текущий ответственный; взять себе чужой диалог менеджер может, если владелец это разрешил. Новому ответственному приходит уведомление в кабинете, в диалог пишется служебная заметка, уходит вебхук `conversation.assigned`.\n\nОшибки: 403 `FORBIDDEN` (у сотрудника ключа нет права назначать или выбран наблюдатель VIEWER), 404 `conversation_not_found`, 409 `CONFLICT` (сотрудник отключён, `is_active: false`, или его нет в команде аккаунта; или диалог одновременно назначили из кабинета: перечитайте и повторите), 422 `VALIDATION_ERROR` (в user_id не только цифры, например «abc»: передайте id строкой из GET /team, например «7»). В сделках и задачах тот же отключённый сотрудник даёт 422 `responsible_user_not_found`: там ответственный проверяется как поле тела запроса. Право ключа: `conversations:write`.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"user_id\": \"7\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "f844955d-4dea-5a64-85c5-f2e33a263818",
     "name": "Диалог",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/conversations/:conversation_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "conversations",
        ":conversation_id"
       ],
       "variable": [
        {
         "key": "conversation_id",
         "value": "<conversation_id>",
         "description": "id диалога: поле id из GET /api/v2/external/conversations или conversation_id из вебхука message.incoming, например 6a7d5d3712f8e1a5c19f40c4"
        }
       ],
       "query": []
      },
      "description": "Диалог\n\nОдин диалог по id: контакт, канал, статус, ответственный, метки, непрочитанные и последнее сообщение. id берут из GET /conversations или из поля `conversation_id` вебхука `message.incoming`.\n\nОшибка 404 `conversation_not_found`: диалога нет в этом аккаунте или он скрыт от сотрудника, чей ключ. Право ключа: `conversations:read`."
     }
    },
    {
     "id": "02d084f4-5fb4-59df-9317-b76c2873d11e",
     "name": "Список диалогов",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/conversations",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "conversations"
       ],
       "variable": [],
       "query": [
        {
         "key": "phone_number_id",
         "value": "<phone_number_id>",
         "description": "Только диалоги этого номера WhatsApp: phone_number_id из GET /api/v2/external/phone-numbers, например 103799815708451. Если передан, channel не учитывается",
         "disabled": true
        },
        {
         "key": "channel",
         "value": "<channel>",
         "description": "Только диалоги канала: whatsapp, telegram, instagram, webchat (чат на сайте), olx, satu, email",
         "disabled": true
        },
        {
         "key": "status",
         "value": "<status>",
         "description": "Только диалоги в статусе: open (открыт, нужно ответить), pending (ожидает), snoozed (отложен), resolved (решён)",
         "disabled": true
        },
        {
         "key": "assignee_user_id",
         "value": "<assignee_user_id>",
         "description": "Только диалоги ответственного: id из GET /api/v2/external/team, например 7; none: диалоги без ответственного",
         "disabled": true
        },
        {
         "key": "unread",
         "value": "<unread>",
         "description": "true: есть непрочитанные сообщения клиента, false: всё прочитано",
         "disabled": true
        },
        {
         "key": "updated_since",
         "value": "<updated_since>",
         "description": "Только диалоги, изменённые после этого момента, ISO 8601, например 2026-10-01T00:00:00Z. Для синхронизации: updated_at последнего диалога прошлой выгрузки",
         "disabled": true
        },
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Диалогов на странице, от 1 до 200",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; без него первая страница",
         "disabled": true
        }
       ]
      },
      "description": "Список диалогов\n\nДиалоги аккаунта из всех каналов (WhatsApp, Telegram, Instagram, чат на сайте, OLX, Satu, почта), отсортированные по `updated_at` от давно изменённых к недавним.\n\n**Синхронизация.** Первый раз выгрузите всё, листая по `next_cursor`, пока `has_more` не станет false. Запомните `updated_at` последнего диалога и в следующий раз передайте его в `updated_since`: придут только диалоги, изменённые после (новое сообщение, статус, ответственный, метки). Диалог, изменённый во время листания, придёт ещё раз в конце, изменения не теряются. Чтобы узнавать сразу, подпишитесь на вебхуки `message.incoming` и `conversation.*`.\n\nФильтры складываются: `status=open&assignee_user_id=none` даёт открытые диалоги без ответственного. Ключ сотрудника видит и меняет то же, что сотрудник в чате кабинета: диалоги по правилам видимости команды и только в доступных ему каналах.\n\nОшибки: 404 `phone_number_not_found` (чужой или неверный phone_number_id), 422 `VALIDATION_ERROR` (неверный assignee_user_id или cursor). Право ключа: `conversations:read`."
     }
    },
    {
     "id": "bbd6acbf-ad9f-5ce5-bdf0-4ccdce4256b3",
     "name": "Переписка диалога",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/conversations/:conversation_id/messages",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "conversations",
        ":conversation_id",
        "messages"
       ],
       "variable": [
        {
         "key": "conversation_id",
         "value": "<conversation_id>",
         "description": "id диалога: поле id из GET /api/v2/external/conversations или conversation_id из вебхука message.incoming, например 6a7d5d3712f8e1a5c19f40c4"
        }
       ],
       "query": [
        {
         "key": "include_notes",
         "value": "<include_notes>",
         "description": "true: добавить заметки сотрудников (is_note: true), клиент их не видит",
         "disabled": true
        },
        {
         "key": "since",
         "value": "<since>",
         "description": "Только сообщения после этого времени, ISO 8601, например 2026-10-18T09:15:00Z: timestamp последнего полученного сообщения",
         "disabled": true
        },
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сообщений на странице, от 1 до 200",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; без него первая страница",
         "disabled": true
        }
       ]
      },
      "description": "Переписка диалога\n\nПереписка диалога от старых сообщений к новым: клиент, менеджеры, сценарии, боты, рассылки и API (кто написал, в `sender.type`). Листайте по `next_cursor`, на последней странице `next_cursor: null`.\n\n**Только новые сообщения.** Передайте в `since` поле `timestamp` последнего полученного сообщения: придут сообщения после него. Сразу узнавать о новых удобнее вебхуком `message.incoming`.\n\nЗаметки сотрудников (клиент их не видит) приходят только с `include_notes=true`, у них `is_note: true`. Ошибка 404 `conversation_not_found`. Право ключа: `conversations:read`."
     }
    },
    {
     "id": "75d3a8d3-0e6b-54d9-8707-b85f05d02377",
     "name": "Задать метки диалога",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/conversations/:conversation_id/labels",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "conversations",
        ":conversation_id",
        "labels"
       ],
       "variable": [
        {
         "key": "conversation_id",
         "value": "<conversation_id>",
         "description": "id диалога: поле id из GET /api/v2/external/conversations или conversation_id из вебхука message.incoming, например 6a7d5d3712f8e1a5c19f40c4"
        }
       ],
       "query": []
      },
      "description": "Задать метки диалога\n\nЗадаёт метки диалога. Список **заменяет прежний целиком**: чтобы добавить метку, прочитайте текущие `tags` из GET /conversations/{conversation_id} и передайте их вместе с новой; `[]` убирает все. Метки копируются в карточку контакта, как в чате. В ответе метки в поле `tags`.\n\nОтдельного вебхука о метках нет: изменение видно по `updated_at` диалога. Ошибки: 403 `FORBIDDEN`, 404 `conversation_not_found`. Право ключа: `conversations:write`.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"labels\": [\n    \"VIP\",\n    \"доставка\"\n  ]\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "d5703da3-add5-5a13-a175-4eda38222b93",
     "name": "Сменить статус диалога",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/conversations/:conversation_id/status",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "conversations",
        ":conversation_id",
        "status"
       ],
       "variable": [
        {
         "key": "conversation_id",
         "value": "<conversation_id>",
         "description": "id диалога: поле id из GET /api/v2/external/conversations или conversation_id из вебхука message.incoming, например 6a7d5d3712f8e1a5c19f40c4"
        }
       ],
       "query": []
      },
      "description": "Сменить статус диалога\n\nМеняет статус диалога: `open` (открыт, нужно ответить), `pending` (ожидает: например, ответа клиента или решения коллеги), `snoozed` (отложен до `snoozed_until`), `resolved` (решён).\n\nЗакрыть диалог: `{\"status\": \"resolved\"}`. Как в чате, клиенту уходит запрос оценки качества, если он включён в настройках, и бот снова отвечает в этом диалоге. Новое сообщение клиента откроет решённый или отложенный диалог само. Уходит вебхук `conversation.status_changed` (если статус действительно изменился).\n\nОшибки: 403 `FORBIDDEN` (у сотрудника только просмотр), 404 `conversation_not_found`. Право ключа: `conversations:write`.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"status\": \"resolved\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    }
   ]
  },
  {
   "id": "a8396740-b256-5c23-9c78-cd3415685fb0",
   "name": "deals",
   "item": [
    {
     "id": "184abc17-9c62-5cfe-84f7-d004a4ddc526",
     "name": "Создать сделку",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/deals",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "deals"
       ],
       "variable": [],
       "query": []
      },
      "description": "Создать сделку\n\nСоздаёт сделку, например из заявки с сайта, так же, как окно «Новая сделка» в кабинете.\n\nДо вызова: pipeline_id и stage_id из GET /pipelines, ответственный из GET /team. Клиент:\ncontact_id из GET /contacts или contact {phone, name}: найдём контакт по номеру или создадим.\nСумма amount целым в тиынах, не в тенге: 150 000 ₸ = 15000000 (в шаблоне WhatsApp\nсумма другая: amount_1000 = 150000000). custom_fields по id полей воронки.\nБез дублей: передайте свой external_id. Повтор формы с тем же external_id, который уже\nесть у сделки аккаунта, сделку не создаёт: 409 deal_exists, id существующей в\ndetails.deal_id. Два одновременных запроса закрывает ещё и заголовок Idempotency-Key:\nшлите тот же ключ при повторе после сбоя сети.\nresponsible_user_id ставит ответственного только сделки: ответственный переписки не\nменяется, для него POST /conversations/{conversation_id}/assign.\nСрабатывают действия этапа и уходит вебхук deal.created. Право deals:write.\nЧастые ошибки: 404 pipeline_not_found, 422 stage_not_found, unknown_custom_field,\namount_field_missing (в воронке нет поля суммы), responsible_user_not_found (нет такого\nсотрудника или он отключён: is_active false в GET /team), VALIDATION_ERROR (нет ни\ncontact_id, ни contact), 404 contact_not_found, 409 deal_exists. Код open_deal_exists\n(у переписки уже есть открытая сделка) этот метод не возвращает: сделка из API не\nпроверяет переписку и создаётся, даже если у клиента есть открытая в этой воронке.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"amount\": 15000000,\n  \"contact\": {\n    \"name\": \"Айгуль Сейткали\",\n    \"phone\": \"77071234567\"\n  },\n  \"currency\": \"KZT\",\n  \"custom_fields\": {\n    \"3e4f5a6b7c8d\": \"Астана\",\n    \"7d8e9f0a1b2c\": \"2026-10-21\",\n    \"9c8b7a6d5e4f\": \"Сайт\"\n  },\n  \"external_id\": \"site-order-4521\",\n  \"pipeline_id\": \"6712a0c4e1b2c3d4e5f60718\",\n  \"responsible_user_id\": \"12\",\n  \"stage_id\": \"a1b2c3d4e5f6a7b8c9d0e1f2\",\n  \"title\": \"Заявка с сайта: кухня на заказ\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "e0a12e98-270f-5d5a-814f-4cd19c98547a",
     "name": "Сделка",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/deals/:deal_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "deals",
        ":deal_id"
       ],
       "variable": [
        {
         "key": "deal_id",
         "value": "<deal_id>",
         "description": "id сделки: поле id из GET /deals или из ответа POST /deals, например 6713b1d5f2c3d4e5f6071829"
        }
       ],
       "query": []
      },
      "description": "Сделка\n\nОдна сделка: этап, сумма в тиынах, клиент, ответственный, свои поля.\n\nСделка чужого аккаунта или скрытая от сотрудника, которому выдан ключ: 404 deal_not_found.\nПраво deals:read."
     }
    },
    {
     "id": "cf4b8d07-adb4-57a8-b9f3-3d10e5f07a7c",
     "name": "Список сделок",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/deals",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "deals"
       ],
       "variable": [],
       "query": [
        {
         "key": "pipeline_id",
         "value": "<pipeline_id>",
         "description": "Только сделки воронки: id из GET /pipelines",
         "disabled": true
        },
        {
         "key": "stage_id",
         "value": "<stage_id>",
         "description": "Только сделки на этапе: stages[].id из GET /pipelines",
         "disabled": true
        },
        {
         "key": "contact_id",
         "value": "<contact_id>",
         "description": "Только сделки клиента: id контакта из GET /contacts",
         "disabled": true
        },
        {
         "key": "external_id",
         "value": "<external_id>",
         "description": "Найти сделку по вашему id (external_id при создании), например site-order-4521: так проверяют дубль перед POST /deals",
         "disabled": true
        },
        {
         "key": "updated_since",
         "value": "<updated_since>",
         "description": "Только изменённые начиная с этого времени, ISO 8601, например 2026-10-18T00:00:00Z",
         "disabled": true
        },
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сколько сделок на странице, от 1 до 200, по умолчанию 50",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; для первой страницы не передавайте",
         "disabled": true
        }
       ]
      },
      "description": "Список сделок\n\nСделки аккаунта по времени изменения, старые первыми, страницами по cursor.\n\nСинхронизация с вашей системой: запомните updated_at последней сделки и в следующий раз\nпередайте его в updated_since. Дубль перед созданием ищут по external_id.\nСумма amount и денежные custom_fields в тиынах (150 000 ₸ = 15000000).\nКлюч сотрудника видит те же сделки, что сотрудник в кабинете. Право deals:read."
     }
    },
    {
     "id": "a8c05e78-3653-5fb9-a2a6-13e22ba58c44",
     "name": "Перенести сделку на этап",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/deals/:deal_id/move",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "deals",
        ":deal_id",
        "move"
       ],
       "variable": [
        {
         "key": "deal_id",
         "value": "<deal_id>",
         "description": "id сделки: поле id из GET /deals или из ответа POST /deals, например 6713b1d5f2c3d4e5f6071829"
        }
       ],
       "query": []
      },
      "description": "Перенести сделку на этап\n\nПереносит сделку на другой этап её воронки, как перетаскивание карточки в кабинете.\n\nstage_id берите из stages[] воронки в GET /pipelines. Перенос на этап с is_won закрывает\nсделку успехом, на этап с is_lost отказом (причину передайте в lost_reason).\nПосле переноса срабатывает то же, что в кабинете: сценарии «Событие в воронке», действия\nэтапа (поставить или закрыть задачи), вебхуки deal.stage_changed и при закрытии deal.closed, передача\nпродажи или заявки в Meta, если клиент пришёл с рекламы «Написать в WhatsApp».\nПоля, обязательные с этого этапа (custom_fields[].required_from_stage_id в GET /pipelines),\nзаполните заранее через PATCH /deals/{deal_id}, иначе\n422 required_fields со списком полей в details. Другая воронка: 422 pipeline_change_not_supported.\nПраво deals:write.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"stage_id\": \"b2c3d4e5f6a7b8c9d0e1f2a3\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "7ca96f57-df0b-5689-867a-0598a4cbe2a4",
     "name": "Изменить сделку",
     "request": {
      "method": "PATCH",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/deals/:deal_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "deals",
        ":deal_id"
       ],
       "variable": [
        {
         "key": "deal_id",
         "value": "<deal_id>",
         "description": "id сделки: поле id из GET /deals или из ответа POST /deals, например 6713b1d5f2c3d4e5f6071829"
        }
       ],
       "query": []
      },
      "description": "Изменить сделку\n\nМеняет название, сумму, ответственного, свои поля и external_id сделки.\n\nМеняются только переданные поля, остальные не трогаются. Сумма amount целым в тиынах.\nВ custom_fields передавайте только меняемые поля, null очищает поле. Снять ответственного\nнельзя: responsible_user_id=null значит «не менять». Ответственный меняется только у\nсделки, не у переписки (её: POST /conversations/{conversation_id}/assign); если в\nкабинете включено правило, новому ответственному ставится задача. Этап этим методом не меняется:\nдля этого POST /deals/{deal_id}/move. В ленте сделки появится «было → стало», уходит\nвебхук deal.changed. Право deals:write.\nЧастые ошибки: 404 deal_not_found, 422 unknown_custom_field, responsible_user_not_found\n(нет такого сотрудника или он отключён), currency_mismatch.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"amount\": 18500000,\n  \"responsible_user_id\": \"15\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    }
   ]
  },
  {
   "id": "8e05041e-cf91-5eb7-adbc-672a238b0d54",
   "name": "media",
   "item": [
    {
     "id": "4b33cb5b-d317-5703-bb97-3a593ed04740",
     "name": "Скачать файл",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/media/:media_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "media",
        ":media_id"
       ],
       "variable": [
        {
         "key": "media_id",
         "value": "<media_id>",
         "description": "id файла: media.id из входящего сообщения (вебхук message.incoming) или из POST /media"
        }
       ],
       "query": [
        {
         "key": "phone_number_id",
         "value": "<phone_number_id>",
         "description": "id номера: phone_number_id из GET /api/v2/external/phone-numbers, например 123456789012345",
         "disabled": false
        }
       ]
      },
      "description": "Скачать файл\n\nСкачивает файл по media_id: тело ответа сам файл, тип в заголовке Content-Type.\n\nТак забирают фото, голосовые и документы, которые прислал клиент. Сохраните файл у себя:\nMeta хранит его ограниченное время. Ошибки: 404 phone_number_not_found или NOT_FOUND (номер не\nнайден; файла с таким id нет в Meta: удалён, срок хранения вышел или id неверный; id sandbox_…\nот тестового ключа, такие файлы не хранятся), 400 BAD_REQUEST (номер не через Cloud API),\n502 provider_error (сбой Meta, повторите позже)."
     }
    },
    {
     "id": "d67c4253-5e05-57b0-bf01-3d96cf301111",
     "name": "Загрузить файл",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/media",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "media"
       ],
       "variable": [],
       "query": [
        {
         "key": "phone_number_id",
         "value": "<phone_number_id>",
         "description": "id номера: phone_number_id из GET /api/v2/external/phone-numbers, например 123456789012345",
         "disabled": false
        }
       ]
      },
      "description": "Загрузить файл\n\nЗагружает файл в WhatsApp и возвращает media_id для отправки в POST /messages/send.\n\nНужен, когда у файла нет публичной ссылки, и для голосовых OGG/OPUS (по ссылке Meta их\nне доставляет). Пример: curl -F \"file=@schet_1024.pdf\" \"…/media?phone_number_id=123456789012345\".\nmedia_id действует 30 дней. Повтор с тем же Idempotency-Key\nне загружает файл второй раз. Ошибки: 400 BAD_REQUEST (номер не через Cloud API),\n413 PAYLOAD_TOO_LARGE, 422 media_unavailable (формат не подходит).",
      "body": {
       "mode": "formdata",
       "formdata": [
        {
         "key": "file",
         "type": "file",
         "src": []
        }
       ]
      }
     }
    }
   ]
  },
  {
   "id": "5745662e-67e1-5847-b1ce-533552635ce2",
   "name": "messages",
   "item": [
    {
     "id": "7dcdf3f0-43be-5201-97ef-9c38c70a83e7",
     "name": "Отметить прочитанными",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/messages/mark-read",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "messages",
        "mark-read"
       ],
       "variable": [],
       "query": []
      },
      "description": "Отметить прочитанными\n\nОтмечает входящие сообщения прочитанными: клиент увидит синие галочки.\n\nПередайте wamid входящих из вебхука message.incoming. Отметка в WhatsApp не меняет\nсчётчик непрочитанных в кабинете. Тестовый ключ ничего не отправляет.\nОшибки: 404 phone_number_not_found, 501 not_supported (номер не через Cloud API).",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"message_ids\": [\n    \"wamid.HBgLNzcwNzEyMzQ1NjcVAgASGBQzQUY5\"\n  ],\n  \"phone_number_id\": \"123456789012345\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "742b3f42-255a-5392-a55a-be28331e39dc",
     "name": "Поставить реакцию",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/messages/reaction",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "messages",
        "reaction"
       ],
       "variable": [],
       "query": []
      },
      "description": "Поставить реакцию\n\nСтавит эмодзи-реакцию на сообщение клиента или своё; пустой emoji снимает реакцию.\n\nMeta принимает реакцию на сообщения не старше 30 дней. Тестовый ключ ничего не отправляет.\nОшибки: 404 phone_number_not_found, 422 invalid_parameters (неверный message_id), 501 not_supported.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"emoji\": \"👍\",\n  \"message_id\": \"wamid.HBgLNzcwNzEyMzQ1NjcVAgASGBQzQUY5\",\n  \"phone_number_id\": \"123456789012345\",\n  \"to\": \"77071234567\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "36b2794b-a69a-55c8-b0c7-fed4dd614b0c",
     "name": "Отправить сообщение",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/messages/send",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "messages",
        "send"
       ],
       "variable": [],
       "query": []
      },
      "description": "Отправить сообщение\n\nОтправляет клиенту сообщение с номера phone_number_id (GET /api/v2/external/phone-numbers).\n\n**Окно 24 часа.** Обычное сообщение (текст, файл, кнопки, место) можно отправить, только если клиент писал вам за последние 24 часа. Первое сообщение и всё вне окна: только одобренный шаблон (type=template, имена в GET /api/v2/external/templates).\n\n**Файлы.** Передайте link (публичная ссылка https) или id: сначала загрузите файл в POST /api/v2/external/media и возьмите media_id из ответа.\n\n**Повтор.** Заголовок Idempotency-Key: повтор с тем же ключом не отправит второе сообщение, а вернёт первый ответ.\n\n**Тестовый ключ** wz_test_… ничего не отправляет и отвечает id вида wamid.SANDBOX….\n\n**После отправки** сообщение появляется в переписке кабинета, а на ваш адрес приходит вебхук message.outgoing; статусы доставки и прочтения приходят вебхуком message.status с тем же message_id.\n\n**Лимиты.** Каждое сообщение расходует лимит тарифа (403 USAGE_LIMIT_EXCEEDED); частоту запросов показывают заголовки X-RateLimit-*. Meta тоже ограничивает частоту (429 rate_limited).\n\n**Как узнать, открыто ли окно.** Окно 24 часа отсчитывается от последнего входящего сообщения клиента: возьмите timestamp последнего сообщения с direction=in из GET /api/v2/external/conversations/{conversation_id}/messages (или время вебхука message.incoming) и прибавьте 24 часа. Позже этого времени отправляйте шаблон.\n\n**Сумма в шаблоне** (type=currency): amount_1000 = тенге × 1000, 150 000 ₸ = 150000000. В сделках сумма в тиынах (× 100): 150 000 ₸ = 15000000.\n\n**Частые ошибки:** 422 outside_24h_window (окно закрыто, отправьте шаблон), 422 template_not_found (нет одобренного шаблона с таким именем и языком; именно 422, а не 404: адрес /messages/send существует, не подходит значение в теле. GET /templates/{template_id} с неизвестным id отвечает 404), 422 invalid_parameters (type не совпадает с блоком, например type=text вместе с template, или файл без id и link), 422 recipient_unavailable (у номера нет WhatsApp), 422 media_unavailable (Meta не скачала файл по ссылке), 404 phone_number_not_found.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"phone_number_id\": \"123456789012345\",\n  \"text\": {\n    \"body\": \"Айгуль, здравствуйте! Запись на 25 октября в 15:00 подтверждена.\"\n  },\n  \"to\": \"77071234567\",\n  \"type\": \"text\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    }
   ]
  },
  {
   "id": "b78f26fe-6b8b-5a37-a8c9-30013faef53a",
   "name": "phone_numbers",
   "item": [
    {
     "id": "9a85e158-f8a5-566c-b469-ede12cec9b71",
     "name": "Подключить номер",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/phone-numbers",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "phone-numbers"
       ],
       "variable": [],
       "query": []
      },
      "description": "Подключить номер\n\nНачинает подключение нового номера WhatsApp.\n\nВ ответе может быть embedded_signup_url: откройте его во всплывающем окне, владелец\nномера войдёт в Facebook и подтвердит номер в Meta. Пока подключение не завершено,\nstatus=pending. Номер расходует лимит устройств тарифа.\nОшибки: 402 DEVICE_QUOTA_EXCEEDED (лимит номеров), 503 SERVICE_UNAVAILABLE\n(подключение через API недоступно, подключите номер в кабинете).",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"display_name\": \"Отдел продаж\",\n  \"phone_e164\": \"+77071234567\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "2d2be2ea-450e-54c1-a3ca-809a622b2c79",
     "name": "Отключить номер",
     "request": {
      "method": "DELETE",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/phone-numbers/:phone_number_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "phone-numbers",
        ":phone_number_id"
       ],
       "variable": [
        {
         "key": "phone_number_id",
         "value": "<phone_number_id>",
         "description": "id номера: поле phone_number_id из GET /api/v2/external/phone-numbers, например 123456789012345"
        }
       ],
       "query": []
      },
      "description": "Отключить номер\n\nОтключает номер от Wazzapo: сообщения с него больше не отправляются и не приходят.\n\nПереписка в кабинете сохраняется. Тестовый ключ номер не отключает.\nОшибка: 404 NOT_FOUND, если номера нет в этом аккаунте."
     }
    },
    {
     "id": "2fa710bf-da9a-5d44-95ec-fb22164d26f9",
     "name": "Номер",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/phone-numbers/:phone_number_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "phone-numbers",
        ":phone_number_id"
       ],
       "variable": [
        {
         "key": "phone_number_id",
         "value": "<phone_number_id>",
         "description": "id номера: поле phone_number_id из GET /api/v2/external/phone-numbers, например 123456789012345"
        }
       ],
       "query": []
      },
      "description": "Номер\n\nВозвращает один номер: статус подключения, имя компании, оценку качества Meta.\n\nПроверяйте status=connected перед рассылкой. Ошибка: 404 NOT_FOUND, если номера\nнет в этом аккаунте."
     }
    },
    {
     "id": "0a6865c5-3251-5046-a0c1-e2976e5eb804",
     "name": "Профиль компании",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/phone-numbers/:phone_number_id/business-profile",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "phone-numbers",
        ":phone_number_id",
        "business-profile"
       ],
       "variable": [
        {
         "key": "phone_number_id",
         "value": "<phone_number_id>",
         "description": "id номера: поле phone_number_id из GET /api/v2/external/phone-numbers, например 123456789012345"
        }
       ],
       "query": []
      },
      "description": "Профиль компании\n\nВозвращает профиль компании в WhatsApp: описание, адрес, почту, сайты, фото.\n\nКлиенты видят его, открыв карточку номера. Только для номеров WhatsApp Cloud API.\nОшибки: 404 NOT_FOUND, 501 not_supported."
     }
    },
    {
     "id": "6fc14520-06a2-5da7-976b-1065b22a6c1c",
     "name": "Список номеров",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/phone-numbers",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "phone-numbers"
       ],
       "variable": [],
       "query": [
        {
         "key": "channel",
         "value": "<channel>",
         "description": "Только этот канал: whatsapp, telegram, instagram, olx, satu, email, webchat. Для отправки сообщений и шаблонов нужен whatsapp",
         "disabled": true
        }
       ]
      },
      "description": "Список номеров\n\nВозвращает номера WhatsApp и другие каналы, подключённые к аккаунту ключа.\n\nС этого метода начинают: phone_number_id из ответа нужен для отправки сообщений,\nзагрузки файлов и шаблонов. Номера WhatsApp идут первыми, за ними остальные каналы\n(поле channel); только WhatsApp: ?channel=whatsapp. Номер подключают в кабинете Wazzapo\nили методом POST /phone-numbers. Отключённые номера в списке не показываются."
     }
    },
    {
     "id": "6e72158a-a3c3-5f3b-ae52-2a509144e762",
     "name": "Изменить профиль компании",
     "request": {
      "method": "PATCH",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/phone-numbers/:phone_number_id/business-profile",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "phone-numbers",
        ":phone_number_id",
        "business-profile"
       ],
       "variable": [
        {
         "key": "phone_number_id",
         "value": "<phone_number_id>",
         "description": "id номера: поле phone_number_id из GET /api/v2/external/phone-numbers, например 123456789012345"
        }
       ],
       "query": []
      },
      "description": "Изменить профиль компании\n\nМеняет профиль компании в WhatsApp. Передайте только поля, которые меняете.\n\nФото профиля этим методом не меняется. Тестовый ключ ничего не меняет.\nОшибки: 404 NOT_FOUND, 422 invalid_parameters (Meta не приняла значение, например\nнеизвестная отрасль vertical), 501 not_supported.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"about\": \"Запись онлайн 24/7\",\n  \"address\": \"Астана, пр. Абая, 15\",\n  \"description\": \"Салон красоты в центре Астаны: массаж, уход за лицом, маникюр.\",\n  \"email\": \"info@example.kz\",\n  \"vertical\": \"BEAUTY\",\n  \"websites\": [\n    \"https://example.kz\"\n  ]\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    }
   ]
  },
  {
   "id": "aee38961-0174-5fe6-b18c-43c443eca141",
   "name": "pipelines",
   "item": [
    {
     "id": "65acb4b0-0c73-5ebf-b0e6-fef0f1beeff1",
     "name": "Список воронок",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/pipelines",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "pipelines"
       ],
       "variable": [],
       "query": [
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сколько воронок на странице, от 1 до 200, по умолчанию 50",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; для первой страницы не передавайте",
         "disabled": true
        }
       ]
      },
      "description": "Список воронок\n\nВоронки аккаунта с этапами по порядку и своими полями сделки.\n\nС этого метода начинают работу со сделками: отсюда берут pipeline_id (data[].id),\nstage_id (stages[].id) и id своих полей для custom_fields (custom_fields[].id, тип и\nварианты options). Поле с is_amount=true хранит сумму сделки amount.\nКлюч сотрудника видит только доступные ему воронки. Право ключа deals:read, без него 403.\nВоронки меняются редко: можно кешировать на час и перечитывать при 422 stage_not_found."
     }
    }
   ]
  },
  {
   "id": "f2cecb42-dc40-5fbc-9c34-0f4fc0f1cbed",
   "name": "tasks",
   "item": [
    {
     "id": "962a4848-fd6e-5ab7-94eb-c4a1dc82b8c4",
     "name": "Поставить задачу по сделке",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/deals/:deal_id/tasks",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "deals",
        ":deal_id",
        "tasks"
       ],
       "variable": [
        {
         "key": "deal_id",
         "value": "<deal_id>",
         "description": "id сделки: поле id из GET /deals или из ответа POST /deals, например 6713b1d5f2c3d4e5f6071829"
        }
       ],
       "query": []
      },
      "description": "Поставить задачу по сделке\n\nСтавит задачу по сделке: перезвонить, встретиться, отправить смету.\n\ndue_at в ISO 8601: Казахстан живёт по UTC+5, «завтра в 10:00 по Астане» это 05:00Z\n(2026-10-19T05:00:00Z) или 2026-10-19T10:00:00+05:00. Исполнитель по умолчанию\nответственный сделки, другой: id из GET /team. Исполнитель получит уведомление и\nнапоминание, уходит вебхук task.created. Повтор с тем же Idempotency-Key задачу не дублирует.\nПраво tasks:write. Ошибки: 404 deal_not_found, 422 responsible_user_not_found.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"description\": \"Уточнить размеры кухни и удобное время замера\",\n  \"due_at\": \"2026-10-19T05:00:00Z\",\n  \"task_type\": \"call\",\n  \"title\": \"Перезвонить Айгуль по замеру\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "0f6b9eb4-d42e-581a-a628-c25827319175",
     "name": "Задачи сделки",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/deals/:deal_id/tasks",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "deals",
        ":deal_id",
        "tasks"
       ],
       "variable": [
        {
         "key": "deal_id",
         "value": "<deal_id>",
         "description": "id сделки: поле id из GET /deals или из ответа POST /deals, например 6713b1d5f2c3d4e5f6071829"
        }
       ],
       "query": [
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сколько задач на странице, от 1 до 200, по умолчанию 50",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; для первой страницы не передавайте",
         "disabled": true
        }
       ]
      },
      "description": "Задачи сделки\n\nЗадачи сделки по сроку, ближайшие первыми, вместе с выполненными (done=true).\n\nСрок due_at в UTC с Z: 05:00Z это 10:00 по Астане. Ключ сотрудника без права видеть чужие\nзадачи получит только свои. Право tasks:read. Нет сделки: 404 deal_not_found."
     }
    },
    {
     "id": "6a8a0681-1802-50be-8bdf-d836f6025022",
     "name": "Изменить задачу",
     "request": {
      "method": "PATCH",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/tasks/:task_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "tasks",
        ":task_id"
       ],
       "variable": [
        {
         "key": "task_id",
         "value": "<task_id>",
         "description": "id задачи: поле id из GET /deals/{deal_id}/tasks или из ответа POST /deals/{deal_id}/tasks"
        }
       ],
       "query": []
      },
      "description": "Изменить задачу\n\nМеняет название, срок или исполнителя задачи, отмечает её выполненной.\n\nМеняются только переданные поля. done=true отмечает выполненной, как кнопка «Выполнено»\nв кабинете, done=false возвращает в работу. id задачи из GET /deals/{deal_id}/tasks.\nПраво tasks:write. Ошибки: 404 task_not_found, 422 responsible_user_not_found.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"done\": true\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    }
   ]
  },
  {
   "id": "40d01278-7df4-577d-9955-3d6389597830",
   "name": "team",
   "item": [
    {
     "id": "8ff106fd-bd1b-51c0-8141-df42845516dc",
     "name": "Сотрудники",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/team",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "team"
       ],
       "variable": [],
       "query": [
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сотрудников на странице, от 1 до 200",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; без него первая страница",
         "disabled": true
        }
       ]
      },
      "description": "Сотрудники\n\nВладелец аккаунта и сотрудники команды: отсюда берут `id` для назначения ответственного (`user_id` в POST /conversations/{conversation_id}/assign) и для упоминаний в заметках (`mention_user_ids`). Первым в списке идёт владелец.\n\nОтключённые сотрудники тоже в списке (`is_active: false`): по ним видно прежних ответственных, но назначить на них нельзя. `phone_number_ids: null` значит, что сотрудник работает во всех номерах. Почты и телефонов сотрудников в ответе нет. Право ключа: `team:read`."
     }
    }
   ]
  },
  {
   "id": "44ae129b-4c86-5d7d-85c0-6079f822377e",
   "name": "templates",
   "item": [
    {
     "id": "f9d3224d-09cd-5ede-9dfe-4e6e6fe7cdcb",
     "name": "Отправить шаблон на проверку",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/templates",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "templates"
       ],
       "variable": [],
       "query": []
      },
      "description": "Отправить шаблон на проверку\n\nСоздаёт шаблон и отправляет его на проверку Meta. Первое сообщение клиенту и сообщение после 24 часов\nтишины можно отправить только одобренным шаблоном.\n\nДо вызова: возьмите phone_number_id из GET /phone-numbers. Ответ приходит со status=PENDING; итог проверки\nузнайте через GET /templates/{template_id} или событие вебхука template.status_updated.\nКартинку, видео или PDF в шапке передайте ссылкой в example.media_url: Wazzapo сам загрузит образец в Meta.\nОшибки: 422 VALIDATION_ERROR (нет обязательного поля), 422 media_unavailable (образец файла не скачался),\n422 invalid_parameters (Meta не приняла шаблон, например имя уже занято), 404 номер не найден.\nПовтор с тем же Idempotency-Key не создаёт второй шаблон.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"category\": \"UTILITY\",\n  \"components\": [\n    {\n      \"example\": {\n        \"body_text\": [\n          [\n            \"Айгуль\",\n            \"20 октября в 15:00\"\n          ]\n        ]\n      },\n      \"text\": \"Здравствуйте, {{1}}! Напоминаем о записи {{2}}. Ждём вас.\",\n      \"type\": \"BODY\"\n    },\n    {\n      \"text\": \"Салон «Ару», Астана\",\n      \"type\": \"FOOTER\"\n    }\n  ],\n  \"language\": \"ru\",\n  \"name\": \"zapis_napominanie\",\n  \"phone_number_id\": \"123456789012345\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "104adfb8-104b-5eeb-a901-df34cbb48357",
     "name": "Удалить шаблон",
     "request": {
      "method": "DELETE",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/templates/:template_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "templates",
        ":template_id"
       ],
       "variable": [
        {
         "key": "template_id",
         "value": "<template_id>",
         "description": "id шаблона в Meta: template_id из POST или GET /api/v2/external/templates, например 1252715608684590"
        }
       ],
       "query": []
      },
      "description": "Удалить шаблон\n\nУдаляет шаблон в Meta (только этот язык) и в Wazzapo. Отменить нельзя.\n\nИмя удалённого одобренного шаблона Meta не даёт занять снова 30 дней; отключённый (DISABLED) шаблон\nMeta не удаляет. Ошибки: 404 шаблон не найден. Тестовый ключ ничего не удаляет."
     }
    },
    {
     "id": "5cf7dbb8-1fa3-54ed-82cf-a298338a9ac2",
     "name": "Шаблон целиком",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/templates/:template_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "templates",
        ":template_id"
       ],
       "variable": [
        {
         "key": "template_id",
         "value": "<template_id>",
         "description": "id шаблона в Meta: template_id из POST или GET /api/v2/external/templates, например 1252715608684590"
        }
       ],
       "query": []
      },
      "description": "Шаблон целиком\n\nШаблон со всеми частями (components), числом переменных и кнопками; статус свежий из Meta.\n\nИспользуйте перед отправкой, чтобы знать, сколько значений и какой файл шапки передать.\nШаблоны, созданные в WhatsApp Manager, появляются здесь после синхронизации в кабинете Wazzapo,\nдо неё ищите их в GET /templates. Ошибки: 404 NOT_FOUND, шаблона с таким id нет в этом\nаккаунте. Это 404, потому что не найден сам адрес-объект; при отправке неизвестного шаблона\nPOST /messages/send отвечает 422 template_not_found (не подошло значение в теле запроса)."
     }
    },
    {
     "id": "84f42b08-f66e-5573-a53d-f902df45d873",
     "name": "Статус шаблона",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/templates/:template_id/status",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "templates",
        ":template_id",
        "status"
       ],
       "variable": [
        {
         "key": "template_id",
         "value": "<template_id>",
         "description": "id шаблона в Meta: template_id из POST или GET /api/v2/external/templates, например 1252715608684590"
        }
       ],
       "query": []
      },
      "description": "Статус шаблона\n\nСвежий статус проверки шаблона в Meta: PENDING, APPROVED, REJECTED, PAUSED или DISABLED.\n\nКороткий вариант для опроса статуса: смотрите в ответе status и rejected_reason. Полное\nсодержимое шаблона (части, переменные, кнопки) берите из GET /templates/{template_id}.\nОпрашивайте после создания или правки, пока статус PENDING (раз в минуту достаточно), или подпишитесь\nна событие template.status_updated. При REJECTED причина в rejected_reason.\nОшибки: 404 NOT_FOUND, шаблона с таким id нет в этом аккаунте (при отправке неизвестного\nшаблона POST /messages/send отвечает 422 template_not_found)."
     }
    },
    {
     "id": "c0a80299-5393-5857-843b-34fc91dd82a7",
     "name": "Список шаблонов",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/templates",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "templates"
       ],
       "variable": [],
       "query": [
        {
         "key": "phone_number_id",
         "value": "<phone_number_id>",
         "description": "id номера из GET /api/v2/external/phone-numbers, например 123456789012345",
         "disabled": false
        },
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сколько шаблонов на странице, от 1 до 200",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа",
         "disabled": true
        },
        {
         "key": "status",
         "value": "<status>",
         "description": "Только с этим статусом: PENDING, APPROVED, REJECTED, PAUSED, DISABLED",
         "disabled": true
        },
        {
         "key": "category",
         "value": "<category>",
         "description": "Только этой категории: MARKETING, UTILITY, AUTHENTICATION",
         "disabled": true
        },
        {
         "key": "name",
         "value": "<name>",
         "description": "Часть имени без учёта регистра, например zapis",
         "disabled": true
        },
        {
         "key": "language",
         "value": "<language>",
         "description": "Только этого языка, например ru или kk",
         "disabled": true
        }
       ]
      },
      "description": "Список шаблонов\n\nВсе шаблоны номера прямо из Meta, со статусом и содержимым (components).\n\nНужен, чтобы выбрать шаблон для messages.send: передайте его name и language, а значений переменных\nстолько, сколько variables_count. Фильтры status, category, name, language можно сочетать.\nСтраницы курсором: next_cursor передайте в cursor. У каналов без шаблонов Meta (почта, OLX, Telegram)\nсписок пустой. Ошибки: 404 номер не найден, 403 account_restricted."
     }
    },
    {
     "id": "0c799769-2268-5c89-937a-dac2a00b80ed",
     "name": "Изменить шаблон",
     "request": {
      "method": "PATCH",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/templates/:template_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "templates",
        ":template_id"
       ],
       "variable": [
        {
         "key": "template_id",
         "value": "<template_id>",
         "description": "id шаблона в Meta: template_id из POST или GET /api/v2/external/templates, например 1252715608684590"
        }
       ],
       "query": []
      },
      "description": "Изменить шаблон\n\nМеняет части шаблона или категорию в Meta, после чего Meta проверяет шаблон заново (status=PENDING).\n\nПравила Meta: править можно шаблон в статусе APPROVED, REJECTED или PAUSED; имя и язык не меняются;\ncomponents заменяются целиком; категорию одобренного шаблона сменить нельзя. Одобренный шаблон можно\nправить 1 раз в сутки и не больше 10 раз за 30 дней, отклонённый без ограничений.\nОшибки: 404 шаблон не найден, 409 CONFLICT (статус не даёт править), 422 VALIDATION_ERROR,\n422 invalid_parameters (Meta отказала, например исчерпан лимит правок). Тестовый ключ ничего не меняет.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"components\": [\n    {\n      \"example\": {\n        \"body_text\": [\n          [\n            \"Айгуль\",\n            \"20 октября в 15:00\"\n          ]\n        ]\n      },\n      \"text\": \"Здравствуйте, {{1}}! Напоминаем о записи {{2}}. Ждём вас.\",\n      \"type\": \"BODY\"\n    },\n    {\n      \"text\": \"Салон «Ару», Астана\",\n      \"type\": \"FOOTER\"\n    }\n  ]\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    }
   ]
  },
  {
   "id": "49610610-8ee1-5760-bc66-30c73f6a14cc",
   "name": "webhook_event_types",
   "item": [
    {
     "id": "efadc138-4002-502b-a44a-6746c97ad74f",
     "name": "Каталог событий",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhook-event-types",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhook-event-types"
       ],
       "variable": [],
       "query": [
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сколько записей на странице",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; без него первая страница",
         "disabled": true
        }
       ]
      },
      "description": "Каталог событий\n\nВсе события, на которые можно подписаться: имя для event_types, описание и пример поля data.\n\nТело запроса на ваш адрес: {\"event\": \"deal.created\", \"timestamp\": \"...\", \"data\": {...}};\nзаголовок X-Wazzapo-Event дублирует имя события. Новые события добавляются без смены версии API.\nСхема каждого поля data, примеры нажатий кнопок и подпись запроса: раздел webhooks в\n/api/v2/external/openapi.json и описание POST /webhooks."
     }
    }
   ]
  },
  {
   "id": "d56e5d8e-a6c6-5a8a-9eb8-38f8b9516f5e",
   "name": "webhooks",
   "item": [
    {
     "id": "5ff671f3-58cc-5f71-9800-57eeaecfaa21",
     "name": "Подписаться на события",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhooks",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhooks"
       ],
       "variable": [],
       "query": []
      },
      "description": "Подписаться на события\n\nПодписывает ваш адрес https на события: Wazzapo шлёт на него POST с JSON.\n\nОтвет содержит `secret`: он показывается один раз, сохраните его. Ключ `wz_test_…` вебхук не создаёт, но отдаёт ответ того же вида.\n\nОшибки: 422 `invalid_event_types` (список: GET /webhook-event-types), 422 адрес не https или внутренний, 403 `USAGE_LIMIT_EXCEEDED` (лимит вебхуков тарифа).\n\n**Тело**: `{\"event\": \"<имя>\", \"timestamp\": \"<когда создана доставка, ISO 8601 UTC>\", \"data\": {...}}`. `timestamp` одинаков во всех повторах; время самого события внутри `data`. Поля `data` у каждого события описаны в разделе Webhooks описания API (openapi.json, ключ `webhooks`).\n\n**Единицы сумм** (пример: 150 000 ₸)\n\n| Где | Поле | Единица | 150 000 ₸ |\n|---|---|---|---|\n| Шаблон, переменная type=currency (messages.send) | `currency.amount_1000` | тенге × 1000 | `150000000` |\n| Сделка в REST (GET/POST/PATCH /deals) | `amount` | тиыны, тенге × 100 | `15000000` |\n| Сделка в вебхуках deal.* | `data.deal.amount` | тиыны, тенге × 100, как в REST | `15000000` |\n| Сделка в вебхуках deal.* | `data.deal.value` | целые тенге, оставлено для совместимости | `150000` |\n| Счёт в вебхуке invoice.status_changed | `data.invoice.amount` | тиыны, тенге × 100 | `15000000` |\n\n## Заголовки запроса\n\n| Заголовок | Что в нём |\n|---|---|\n| `X-Wazzapo-Timestamp` | Время отправки этой попытки: Unix-время в секундах, целое число, UTC, например `1760000000`. В каждой попытке новое |\n| `X-Wazzapo-Signature-V2` | `v2=` и 64 символа hex строчными буквами: HMAC-SHA256 от `{X-Wazzapo-Timestamp}.{тело}`. Проверяйте этот заголовок |\n| `X-Wazzapo-Signature` | Старая подпись без времени: `sha256=` и hex строчными, HMAC-SHA256 от тела. Оставлена для совместимости, не защищает от повтора перехваченного запроса |\n| `X-Wazzapo-Delivery-Id` | id доставки, одинаковый во всех повторах: по нему отсеивайте дубли. Его же принимает POST /webhooks/{webhook_id}/deliveries/{delivery_id}/retry |\n| `X-Wazzapo-Event` | Имя события, то же, что поле `event` тела |\n| `X-Wazzapo-Attempt` | Номер попытки: 1 у первой, до 6 |\n| `Content-Type` | `application/json`; `User-Agent: Wazzapo-Webhooks/1.0` |\n\nУ тестового события (POST /webhooks/{webhook_id}/test, event=test) нет `X-Wazzapo-Delivery-Id` и `X-Wazzapo-Attempt`.\n\n**Как проверить подпись**\n\n1. Возьмите тело запроса сырыми байтами, как пришло, до разбора JSON. Не собирайте JSON заново: порядок полей, отсутствие пробелов и кириллица в UTF-8 (без `\\u`-экранирования) должны совпасть байт в байт.\n2. Строка для подписи: значение `X-Wazzapo-Timestamp`, точка, тело: `{timestamp}.{body}`.\n3. Ключ HMAC: секрет вебхука целиком, вместе с `whsec_`, в UTF-8 (из ответа POST /webhooks или rotate-secret).\n4. Ожидаемое значение: `v2=` + HMAC-SHA256(ключ, строка) в hex строчными буквами.\n5. Сравните с `X-Wazzapo-Signature-V2` сравнением за постоянное время (`hash_equals`, `crypto.timingSafeEqual`, `hmac.compare_digest`). Не совпало: ответьте 401.\n6. Отклоните запрос, если `X-Wazzapo-Timestamp` отличается от вашего времени больше чем на 300 секунд.\n\n**Пример с готовыми значениями**: секрет `whsec_test`, `X-Wazzapo-Timestamp: 1760000000`, тело `{\"event\":\"message.incoming\"}` (28 байт). Строка для подписи `1760000000.{\"event\":\"message.incoming\"}`, `X-Wazzapo-Signature-V2: v2=a7e2dad2ec4a0f54d4f46ea51f2c28803b66f4effebb959c6ff8e8cc59f47fca`, старая `X-Wazzapo-Signature: sha256=b8745e4a15618d503f14d3e7fb12aa3c5cf3064a4bbc50b5f515eae87ee76cd7`.\n\n**PHP**\n\n```php\n$body = file_get_contents('php://input');           // сырое тело\n$ts   = $_SERVER['HTTP_X_WAZZAPO_TIMESTAMP'] ?? '';\n$sig  = $_SERVER['HTTP_X_WAZZAPO_SIGNATURE_V2'] ?? '';\n$expected = 'v2=' . hash_hmac('sha256', $ts . '.' . $body, $secret);\nif (!hash_equals($expected, $sig) || abs(time() - (int)$ts) > 300) { http_response_code(401); exit; }\n$event = json_decode($body, true);\nhttp_response_code(200);\n```\n\n**Node.js (Express)**\n\n```js\napp.post('/hooks/wazzapo', express.raw({ type: 'application/json' }), (req, res) => {\n  const ts = req.get('X-Wazzapo-Timestamp') || '';\n  const sig = req.get('X-Wazzapo-Signature-V2') || '';\n  const expected = 'v2=' + crypto.createHmac('sha256', SECRET)\n    .update(ts + '.').update(req.body).digest('hex');   // req.body: Buffer\n  const ok = sig.length === expected.length &&\n    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));\n  if (!ok || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401);\n  res.sendStatus(200);                                 // ответ сразу, обработка потом\n  const event = JSON.parse(req.body.toString('utf8'));\n});\n```\n\n**Ответ и повторы.** Ответьте любым 2xx за 10 секунд; тело ответа не важно. Иначе повторы через 1 мин, 5 мин, 30 мин, 2 ч и 6 ч (±20%), всего 6 попыток, с тем же телом и `X-Wazzapo-Delivery-Id`. Доставка «хотя бы один раз»: дубли возможны, отсеивайте их по `X-Wazzapo-Delivery-Id`. Порядок событий не гарантирован: сверяйте поле `timestamp`. После 10 доставок подряд, не дошедших за все попытки, вебхук выключается (`is_active=false`), включить снова: PATCH с `is_active=true`.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"description\": \"CRM: заявки\",\n  \"event_types\": [\n    \"message.incoming\",\n    \"deal.stage_changed\"\n  ],\n  \"url\": \"https://crm.example.kz/hooks/wazzapo\"\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    },
    {
     "id": "2e1f7bdd-410f-52e5-9a41-422db8cba1d7",
     "name": "Удалить вебхук",
     "request": {
      "method": "DELETE",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhooks/:webhook_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhooks",
        ":webhook_id"
       ],
       "variable": [
        {
         "key": "webhook_id",
         "value": "<webhook_id>",
         "description": "id вебхука: поле id из ответа POST /webhooks или из GET /webhooks, например 6712f0c4a1b2c3d4e5f60719"
        }
       ],
       "query": []
      },
      "description": "Удалить вебхук\n\nУдаляет вебхук вместе с историей доставок: события на адрес больше не приходят.\n\nЧтобы остановить на время, лучше выключите: PATCH с is_active=false. Ответ 204 без тела.\nОшибки: 404 webhook_not_found."
     }
    },
    {
     "id": "6ff33d29-b8cb-51f9-926c-b8ff97cac9f0",
     "name": "Вебхук",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhooks/:webhook_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhooks",
        ":webhook_id"
       ],
       "variable": [
        {
         "key": "webhook_id",
         "value": "<webhook_id>",
         "description": "id вебхука: поле id из ответа POST /webhooks или из GET /webhooks, например 6712f0c4a1b2c3d4e5f60719"
        }
       ],
       "query": []
      },
      "description": "Вебхук\n\nОдин вебхук: адрес, события, включён ли и сколько доставок подряд не дошли. Без секрета.\n\nОшибки: 404 webhook_not_found."
     }
    },
    {
     "id": "937f9d69-53f6-5375-9123-2eb12f080a31",
     "name": "Список вебхуков",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhooks",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhooks"
       ],
       "variable": [],
       "query": [
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сколько записей на странице",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; без него первая страница",
         "disabled": true
        }
       ]
      },
      "description": "Список вебхуков\n\nВсе вебхуки аккаунта: созданные через API v1, v2 и в кабинете. Секреты не показываются.\n\nСмотрите is_active и consecutive_failures: так видно вебхук, выключенный после 10 провалов подряд.\nОшибки: 401 неверный ключ."
     }
    },
    {
     "id": "d546a911-e56e-535c-9190-7a9029f84cf6",
     "name": "История доставок",
     "request": {
      "method": "GET",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhooks/:webhook_id/deliveries",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhooks",
        ":webhook_id",
        "deliveries"
       ],
       "variable": [
        {
         "key": "webhook_id",
         "value": "<webhook_id>",
         "description": "id вебхука: поле id из ответа POST /webhooks или из GET /webhooks, например 6712f0c4a1b2c3d4e5f60719"
        }
       ],
       "query": [
        {
         "key": "limit",
         "value": "<limit>",
         "description": "Сколько записей на странице",
         "disabled": true
        },
        {
         "key": "cursor",
         "value": "<cursor>",
         "description": "next_cursor из прошлого ответа; без него первая страница",
         "disabled": true
        }
       ]
      },
      "description": "История доставок\n\nИстория доставок вебхука, новые первыми: событие, статус, номер попытки, ответ вашего адреса.\n\nСмотрите здесь, почему события не доходят (error_message, response_status) и когда следующая\nпопытка (next_retry_at). Не дошедшую доставку можно отправить ещё раз методом retry.\nОшибки: 404 webhook_not_found."
     }
    },
    {
     "id": "f6fecbfa-050c-5c1c-abf1-d06e495f6a2b",
     "name": "Повторить доставку",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhooks/:webhook_id/deliveries/:delivery_id/retry",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhooks",
        ":webhook_id",
        "deliveries",
        ":delivery_id",
        "retry"
       ],
       "variable": [
        {
         "key": "webhook_id",
         "value": "<webhook_id>",
         "description": "id вебхука: поле id из ответа POST /webhooks или из GET /webhooks, например 6712f0c4a1b2c3d4e5f60719"
        },
        {
         "key": "delivery_id",
         "value": "<delivery_id>",
         "description": "id доставки: поле id из GET /webhooks/{webhook_id}/deliveries или заголовок X-Wazzapo-Delivery-Id запроса, пришедшего на ваш адрес"
        }
       ],
       "query": []
      },
      "description": "Повторить доставку\n\nСразу отправляет доставку ещё раз, не дожидаясь расписания повторов, и ждёт ответ.\n\nНужен, когда вы починили приёмник и не хотите ждать следующей попытки. Тело и\nX-Wazzapo-Delivery-Id те же, что раньше: дубль отсеется по нему. Счётчик attempt растёт.\nОшибки: 404 webhook_not_found, 404 webhook_delivery_not_found."
     }
    },
    {
     "id": "e9b47a0f-07dd-5476-8a6c-b4a5baab3ab9",
     "name": "Сменить секрет подписи",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhooks/:webhook_id/rotate-secret",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhooks",
        ":webhook_id",
        "rotate-secret"
       ],
       "variable": [
        {
         "key": "webhook_id",
         "value": "<webhook_id>",
         "description": "id вебхука: поле id из ответа POST /webhooks или из GET /webhooks, например 6712f0c4a1b2c3d4e5f60719"
        }
       ],
       "query": []
      },
      "description": "Сменить секрет подписи\n\nВыдаёт новый секрет подписи, старый перестаёт действовать сразу.\n\nНужен, если секрет потерян или мог утечь. Новый секрет показывается один раз: сразу замените\nего на своём сервере, иначе проверка подписи у вас перестанет проходить. Ошибки: 404 webhook_not_found."
     }
    },
    {
     "id": "df8f5cde-8b9b-5453-8bd0-61ebb77be70b",
     "name": "Отправить тестовое событие",
     "request": {
      "method": "POST",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Idempotency-Key",
        "value": "{{$guid}}"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhooks/:webhook_id/test",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhooks",
        ":webhook_id",
        "test"
       ],
       "variable": [
        {
         "key": "webhook_id",
         "value": "<webhook_id>",
         "description": "id вебхука: поле id из ответа POST /webhooks или из GET /webhooks, например 6712f0c4a1b2c3d4e5f60719"
        }
       ],
       "query": []
      },
      "description": "Отправить тестовое событие\n\nСразу отправляет на адрес вебхука тестовое событие test с настоящей подписью и ждёт ответ.\n\nПроверьте так приёмник и проверку подписи до боевых событий. Повторов нет, в историю не пишется.\nРезультат в ответе: delivered, код и время ответа, ошибка. Ошибки: 404 webhook_not_found."
     }
    },
    {
     "id": "1c36a9da-fec3-558c-8e95-66700ec4db71",
     "name": "Изменить вебхук",
     "request": {
      "method": "PATCH",
      "header": [
       {
        "key": "Accept",
        "value": "application/json"
       },
       {
        "key": "Content-Type",
        "value": "application/json"
       }
      ],
      "url": {
       "raw": "{{baseUrl}}/api/v2/external/webhooks/:webhook_id",
       "host": [
        "{{baseUrl}}"
       ],
       "path": [
        "api",
        "v2",
        "external",
        "webhooks",
        ":webhook_id"
       ],
       "variable": [
        {
         "key": "webhook_id",
         "value": "<webhook_id>",
         "description": "id вебхука: поле id из ответа POST /webhooks или из GET /webhooks, например 6712f0c4a1b2c3d4e5f60719"
        }
       ],
       "query": []
      },
      "description": "Изменить вебхук\n\nМеняет адрес, события, подпись или включает и выключает вебхук. Непереданные поля не меняются.\n\nevent_types заменяют весь список. is_active=false выключает доставку, is_active=true включает\nснова и обнуляет счётчик провалов (так включают вебхук после автоотключения). Секрет не меняется.\nОшибки: 404 webhook_not_found, 422 invalid_event_types, 422 адрес не https.",
      "body": {
       "mode": "raw",
       "raw": "{\n  \"event_types\": [\n    \"message.incoming\",\n    \"deal.stage_changed\",\n    \"deal.closed\"\n  ]\n}",
       "options": {
        "raw": {
         "language": "json"
        }
       }
      }
     }
    }
   ]
  }
 ]
}
