> Расположение: Исходящие вебхуки
> Краткое содержание: Как разработать и запустить обработчик исходящих вебхуков Пачки: отдельный бот для разработки, поток событий без публичного адреса, туннель через ngrok и Cloudflare Tunnel, чек-лист перед запуском в работу, serverless и холодный старт при коротких сроках ответа
> Это Markdown-версия конкретной страницы. Для контекста за её пределами (правила API, полный перечень методов, авторизация) ОБЯЗАТЕЛЬНО открой [llms.txt](https://dev.pachca.com/llms.txt) перед ответом — это сэкономит токены и предотвратит неполный ответ.


# Разработка и запуск обработчика

Обработчик принимает события Пачки по адресу из поля **Webhook URL** на вкладке **Исходящий вебхук** в карточке бота. Пачка отправляет их на публичный адрес `http` или `https`, а `localhost` и адреса внутренней сети поле не принимает, подробнее в разделе [Общие настройки](/guides/webhook/events#obschie-nastroyki). Пока обработчик работает на вашей машине, события получают потоком без публичного адреса или через туннель.

Здесь — оба способа, чек-лист перед запуском в работу и что учесть в serverless. Как написать сам обработчик, описано в разделах [Безопасность и обработчик](/guides/webhook/handler) и [Первый бот](/guides/bots/first-bot).

## Отдельный бот для разработки

У бота один **Webhook URL** и одна история событий. Если разрабатывать на рабочем боте, адрес вашей машины заменит адрес сервера, и события пойдут к вам. Если подключить разработку к потоку рабочего бота, на каждое событие ответят и сервер, и ваш код, а удаляя обработанные записи, вы уберёте из истории события, которые сервер забирает после сбоя.

Заведите для разработки отдельного бота с теми же событиями и правами, что у рабочего, и добавьте его в тестовый чат. Как это сделать — в разделе [Создание бота](/guides/bots/setup).

## Как получать события на своей машине

Оба способа доставляют одни и те же события. Различаются настройка бота и то, насколько код похож на рабочий.

| Что | Поток событий | Туннель |
| --- | --- | --- |
| Публичный адрес | Не нужен | Временный адрес туннеля в **Webhook URL** |
| Сторонний сервис | Не нужен | ngrok, Cloudflare Tunnel или другой |
| Что включить у бота | **Сохранять историю событий**, адрес вебхука пустой | Исходящий вебхук с адресом туннеля |
| Права токена бота сверх рабочих | `webhooks:events:read` и `webhooks:events:delete` | `bot_self:webhook:write`, если адрес прописывает сам бот |
| Проверка подписи | Не нужна, доступ даёт сама ссылка | Та же, что на рабочем сервере |
| Ответ на отправку формы | Методом [Ответ на отправку формы](/api/views/submit-response) | Кодом ответа на вебхук |

Потоку не нужны сторонний сервис и публичный адрес, поэтому с него проще начать. Туннель проверяет тот же код, что пойдёт на сервер, вместе с подписью и кодами ответа.

Логику бота удобно держать в функции, которая принимает payload события. В потоке payload — тот же JSON, что приходит на адрес вебхука, поэтому одна функция работает с обоими способами. Отличается ответ на отправку формы: в потоке его отправляют методом API, а на вебхук — кодом ответа, подробнее в разделе [Ответ через журнал событий](/guides/forms/handling#otvet-cherez-zhurnal-sobytiy).

## Разработка без публичного адреса

Метод [Поток событий](/api/bots/get-stream) отдаёт ссылку, по которой события бота приходят сразу, как они происходят. Подключение к ней исходящее, поэтому публичный адрес и туннель не нужны, а на нажатие кнопки и отправку формы бот успевает ответить в срок.


  ### Шаг 1. Настройте бота разработки

В поток попадают те же события, что ложатся в историю, поэтому у бота разработки включите **Сохранять историю событий** и отметьте нужные события. Адрес вебхука оставьте пустым: тогда на отправку формы бот отвечает методом API, как в таблице выше. Вкладка **Исходящий вебхук** показывает настройки только вместе с адресом, поэтому боту без адреса их задают методом [Редактирование бота](/api/bots/update) с персональным токеном, подробнее в разделе [Через API](/guides/webhook/events#cherez-api). Новый бот слушает только команды, поэтому для проверки обычным сообщением выберите режим **Любые сообщения**, в API это `all_messages`.

```bash title="Настройка бота разработки через CLI"
# 1738816 — id бота разработки, команда работает с персональным токеном
pachca bots update 1738816 --events-history-enabled --trigger-on all_messages \
  --events '["message_new","button_click"]'
```


  ### Шаг 2. Добавьте права токену бота

Для потока токену бота нужно право `webhooks:events:read`, а чтобы удалять обработанные события из истории — `webhooks:events:delete`. Остальные права — как у рабочего бота, подробнее в разделе [Наборы прав под типовые задачи](/guides/permissions#nabory-prav-pod-tipovye-zadachi).


  ### Шаг 3. Посмотрите события в терминале

Получите ссылку через [CLI](/guides/cli/overview) и откройте её: события бота пойдут в терминал по мере прихода. Ссылка даёт доступ к событиям без токена, поэтому храните её как секрет.

```bash title="Поток событий в терминале"
# PACHCA_BOT_TOKEN — токен бота разработки
STREAM_URL=$(PACHCA_TOKEN=$PACHCA_BOT_TOKEN pachca bots get-stream -o json | jq -r '.url')
curl -N "$STREAM_URL"
```

Если команда ответила `404`, у бота выключена история событий или поток недоступен.


  ### Шаг 4. Подключите обработчик

В коде подключайтесь к потоку в порядке, при котором события не теряются: дождитесь подтверждения подписки, дочитайте историю и меняйте ссылку до `expires_at`. Порядок и готовый пример на TypeScript — в разделе [События в реальном времени](/guides/webhook/polling#sobytiya-v-realnom-vremeni).


## Разработка с туннелем

Туннель выдаёт порту на вашей машине временный публичный адрес `https`. Пачка отправляет события на этот адрес, а туннель передаёт их обработчику.


  ### Шаг 1. Запустите обработчик

Запустите обработчик на своей машине с той же проверкой подписи и времени, что и на сервере. Примеры на TypeScript и Python — в разделе [Реализация webhook handler](/guides/webhook/handler#realizatsiya-webhook-handler). Команды ниже открывают туннель к порту `3000`, как в примере на TypeScript. Если обработчик слушает другой порт, укажите его.


  ### Шаг 2. Откройте туннель

Подойдёт любой туннель с адресом `https`. Ниже два распространённых варианта, подробности установки — в документации [ngrok](https://ngrok.com/docs/getting-started/) и [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/trycloudflare/).

Для ngrok нужен аккаунт ngrok и его токен из личного кабинета. Публичный адрес ngrok показывает в строке `Forwarding`. Бесплатный тариф даёт один постоянный адрес на домене ngrok, он не меняется между запусками, а свой домен доступен на платных тарифах.

```bash title="ngrok"
brew install ngrok    # macOS, другие системы — в документации ngrok
ngrok config add-authtoken YOUR_NGROK_TOKEN
ngrok http 3000
```

Быстрому туннелю Cloudflare не нужны ни аккаунт, ни свой домен. Адрес вида `https://….trycloudflare.com` cloudflared выводит в консоль, и при каждом запуске он новый. Cloudflare предлагает быстрые туннели для тестов и разработки, а постоянный адрес на своём домене даёт именованный туннель.

```bash title="Cloudflare Tunnel"
brew install cloudflared    # macOS, другие системы — в документации Cloudflare
cloudflared tunnel --url http://localhost:3000
```


  ### Шаг 3. Пропишите адрес туннеля боту

В **Webhook URL** укажите адрес туннеля с путём обработчика, например `https://TUNNEL_HOST/webhook`. Впишите его на вкладке **Исходящий вебхук** или передайте самим ботом: токеном бота с правом `bot_self:webhook:write` через CLI или методом [Саморегистрация вебхука бота](/api/bots/update-webhook). Команду удобно запускать каждый раз, когда у туннеля новый адрес.

```bash title="Адрес туннеля через CLI"
PACHCA_TOKEN=$PACHCA_BOT_TOKEN pachca bots update-webhook --outgoing-url https://TUNNEL_HOST/webhook
```

```bash title="Адрес туннеля через API"
curl -X PUT https://api.pachca.com/api/shared/v1/bot/webhook \
  -H "Authorization: Bearer $PACHCA_BOT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"webhook": {"outgoing_url": "https://TUNNEL_HOST/webhook"}}'
```

Боту с разворачиванием ссылок адрес меняет администратор или владелец пространства, подробнее в разделе [Через API](/guides/webhook/events#cherez-api).


  ### Шаг 4. Проверьте доставку

Отметьте нужные события на вкладке **Исходящий вебхук** и напишите боту в тестовом чате. Новый бот слушает только команды, поэтому для проверки обычным сообщением выберите режим **Любые сообщения**, подробнее в разделе [Сообщения](/guides/webhook/events#soobscheniya).


Пока обработчик на вашей машине остановлен, туннель сам отвечает Пачке ошибкой, а событие, на которое пришла ошибка, Пачка повторно не отправляет, подробнее в разделе [Доставка и повторы](/guides/webhook/handler#dostavka-i-povtory). Включите у бота разработки историю событий, чтобы забрать пропущенное.

> Запросы приходят к обработчику с адреса туннеля, поэтому на своей машине проверку IP-адреса отключите. Подпись и время проверяйте как обычно.


## Чек-лист перед запуском в работу

Пройдите по списку, прежде чем указать в **Webhook URL** адрес рабочего сервера.


  ### Шаг 1. Безопасность

- **HTTPS с действующим сертификатом.** Поле принимает и `http`, но по нему тело события и подпись идут открытым текстом. Пачка проверяет сертификат сервера, поэтому с самоподписанным сертификатом событие не дойдёт. Указывайте конечный адрес: по перенаправлениям Пачка не переходит, подробнее в разделе [Доставка и повторы](/guides/webhook/handler#dostavka-i-povtory).
- **Проверка подписи и времени.** Проверяйте подпись из заголовка `Pachca-Signature` по сырому телу запроса сравнением за постоянное время и отклоняйте запросы со старым `webhook_timestamp`. Как это сделать — в разделе [Безопасность](/guides/webhook/handler#bezopasnost).
- **Белый список IP-адресов.** Принимайте запросы с адресов, с которых Пачка отправляет вебхуки: список и ссылка на его актуальную версию — в разделе [Безопасность](/guides/webhook/handler#bezopasnost). За балансировщиком или обратным прокси адрес отправителя берите из заголовка, который добавляет ваш прокси, например `X-Forwarded-For`, и доверяйте этому заголовку, только когда запрос пришёл от вашего прокси.
- **Секреты в переменных окружения.** Токен бота и **Signing secret** передавайте обработчику через переменные окружения или хранилище секретов, подробнее в разделе [Если токен утёк](/guides/bots/tokens#esli-token-utyok). Если утёк **Signing secret**, удалите вебхук кнопкой **Удалить вебхук** и включите заново: секрет выпустится новый, а остальные настройки вкладки вернутся к начальным, подробнее в разделе [Общие настройки](/guides/webhook/events#obschie-nastroyki).


  ### Шаг 2. Надёжность

- **Быстрый ответ и очередь.** Отвечайте `200` сразу, а запросы к API и другую долгую работу передавайте в фоновую очередь: на ошибку или опоздание Пачка событие не повторяет. Две вещи очередь не ждут: форму по `trigger_id` открывают в первые три секунды после нажатия, а ошибки в полях формы возвращают ответом на её отправку, тоже за три секунды. Все сроки — в разделе [Сроки ответа](/guides/webhook/handler#sroki-otveta).
- **Ключи идемпотентности в хранилище.** Одно событие изредка приходит дважды. Храните ключи обработанных событий в базе или Redis со сроком жизни, например сутки: память процесса теряется при перезапуске и у каждого экземпляра своя. Как собрать ключ для каждого типа события — в разделе [Идемпотентная обработка](/guides/webhook/handler#idempotentnaya-obrabotka).
- **История событий как страховка.** Включите **Сохранять историю событий**, чтобы после простоя сервера или ответа с ошибкой забрать пропущенное методом [История событий](/api/bots/list-events). История сама не очищается, поэтому обработанные записи удаляйте методом [Удаление события истории](/api/bots/remove-event). Токену бота для этого нужны права `webhooks:events:read` и `webhooks:events:delete`. Подробнее — в разделе [История при работающем вебхуке](/guides/webhook/polling#istoriya-pri-rabotayuschem-vebhuke).


  ### Шаг 3. Настройки бота

- **Свои сообщения.** Включите **Игнорировать свои сообщения**, чтобы ответ бота не запускал обработчик снова. Подробнее — в разделе [Общие настройки](/guides/webhook/events#obschie-nastroyki).
- **Отдельный бот.** Рабочий сервер и разработка работают с разными ботами, как в разделе [Отдельный бот для разработки](#otdelnyy-bot-dlya-razrabotki). Так адрес вашей машины не заменит адрес сервера.


## Serverless и холодный старт

Платформа serverless запускает обработчик по запросу. Первый запрос после простоя ждёт холодного старта: платформа поднимает окружение и загружает код с зависимостями, и это может занять несколько секунд.

На большинство событий запаса хватает, но два срока короткие: форму по `trigger_id` открывают в первые три секунды после нажатия кнопки, а на отправку формы отвечают за три секунды, подробнее в разделе [Сроки ответа](/guides/webhook/handler#sroki-otveta). Если холодный старт занял это время, форма не откроется, а при отправке формы сотрудник увидит ошибку.

- **Тёплый экземпляр.** Держите хотя бы один экземпляр функции запущенным: в настройках платформы это минимальное число экземпляров или зарезервированные экземпляры. Боту с формами это помогает надёжнее всего.
- **Форма первым делом.** В обработчике нажатия вызывайте [Открытие представления](/api/views/open) сразу после проверки подписи, до остальной работы. Подключение к базе и тяжёлые модули загружайте после этого вызова.
- **Работа после ответа в очереди платформы.** Многие платформы приостанавливают функцию сразу после ответа, и фоновая работа в том же процессе может не завершиться. Передавайте её в очередь сообщений или в отдельную функцию.
- **Ключи во внешнем хранилище.** Экземпляры функции не делят память, поэтому ключи идемпотентности храните в базе или Redis.
- **Вебхук вместо потока.** Поток событий держит долгое подключение, а функция работает ограниченное время. В serverless принимайте события вебхуком, а поток подключайте на постоянно работающем сервере.

Если уложиться в три секунды не получается, принимайте события на постоянно работающем сервере: он сам открывает формы и отвечает на их отправку, а остальную работу передаёт в serverless через очередь.


## Связанные разделы

- [Боты: Первый бот](/guides/bots/first-bot)
- [Безопасность и обработчик](/guides/webhook/handler)
- [Поллинг](/guides/webhook/polling)
- [Настройка и типы событий](/guides/webhook/events)
