Почему webhook-обработчики должны отвечать быстро, а обрабатывать позже

Webhook-обработчики должны быстро подтверждать приём и выносить медленную работу в фоновые задачи. Объясняем, почему этот паттерн повышает надёжность и снижает число дубликатов событий.

Почему webhook-обработчики должны отвечать быстро, а обрабатывать позже

Частая ошибка в обработке webhook — выполнять слишком много работы прямо внутри обработчика запроса.

Провайдер отправляет событие. Ваш эндпоинт его получает. Затем обработчик проверяет событие, обращается к базе данных, вызывает внешние API, отправляет письмо, генерирует отчёт и, может быть, обращается к LLM. И только после всего этого возвращает 200 OK.

Это хрупко.

Webhook-обработчики обычно должны отвечать быстро, а обрабатывать позже.

Провайдеру webhook нужно лишь подтверждение

Большинству webhook-провайдеров в первую очередь нужно знать одно:

Did you receive the event?

Им не обязательно, чтобы весь ваш последующий workflow завершился до того, как вы ответите.

Более удачный поток обработчика такой:

receive event
→ verify signature
→ store event or start job
→ return 200
→ process later

Провайдер получает быстрое подтверждение. А у вашей системы есть время безопасно завершить работу.

Что идёт не так, когда обработчики медленные

Таймаут на стороне провайдера

Если ваш эндпоинт отвечает слишком долго, провайдер может решить, что доставка не удалась. Он может повторить то же событие, вызвав повторную обработку.

Дублирующиеся побочные эффекты

Дубликат webhook может создать дублирующиеся записи, отправить повторные письма или запустить повторную биллинговую логику — если только ваша обработка не идемпотентна.

Плохой пользовательский опыт

Медленные обработчики сложнее отлаживать. Пользователь может завершить действие в одной системе, но не увидеть результата в вашей, потому что обработчик отвалился по таймауту на полпути.

Хрупкая цепочка зависимостей

Если ответ вашего webhook зависит от пяти нижестоящих сервисов, любой из них может сорвать доставку от провайдера целиком.

Паттерн быстрого подтверждения

Обработчик должен выполнять только необходимый минимум работы:

  1. распарсить запрос;
  2. проверить подпись;
  3. проверить тип события;
  4. сохранить событие или создать задачу;
  5. вернуть успех.

Всё остальное может происходить в фоновой задаче.

Provider request
→ Webhook route
→ Event stored
→ 200 OK

Background job
→ Business logic
→ External APIs
→ Notifications
→ Logs

Этот паттерн упрощает управление сбоями, потому что доставка от провайдера и внутренняя обработка разделены.

Пример: webhook об оплате

Плохой паттерн:

Stripe event
→ verify
→ create order
→ generate invoice
→ send email
→ update CRM
→ return 200

Лучший паттерн:

Stripe event
→ verify
→ store event.id
→ start fulfill-order job
→ return 200

fulfill-order job
→ create order
→ generate invoice
→ send email
→ update CRM
→ mark event processed

Если вызов CRM упадёт, провайдеру не нужно присылать событие заново. Ваша внутренняя задача может повторить попытку.

Пример: AI-workflow через webhook

Предположим, webhook запускает AI-workflow:

new support ticket
→ classify urgency
→ summarize conversation
→ suggest reply
→ notify team

Это может быть медленно. Здесь могут быть несколько вызовов модели и запросов к внешним API. Запускать это прямо внутри webhook-запроса рискованно.

Более удачный поток:

/support/webhook
→ verify event
→ create classification job
→ return 200

classification job
→ retrieve ticket
→ call LLM
→ store result
→ notify Slack

Webhook-эндпоинт остаётся быстрым. А AI-работа становится наблюдаемой.

Почему это улучшает повторные попытки

Повторные попытки провайдера и внутренние повторные попытки не должны быть одним и тем же механизмом.

Повторная попытка провайдера означает:

the provider could not deliver the event

Внутренняя повторная попытка означает:

your system received the event but processing failed

Это разные проблемы. Их смешение порождает путаницу.

Быстрое подтверждение позволяет вашей платформе взять событие под свою ответственность сразу после получения.

Что сохранить перед ответом

Прежде чем вернуть 200, сохраните достаточно данных, чтобы обработать событие позже:

  • имя провайдера;
  • ID события;
  • тип события;
  • сырой или нормализованный payload;
  • метку времени получения;
  • результат проверки подписи;
  • статус обработки;
  • контекст тенанта или аккаунта.

Не всегда нужно хранить весь сырой payload вечно, но контекста должно хватать для повторной попытки и отладки.

Где здесь Inquir Compute

Inquir Compute поддерживает этот паттерн с помощью API-роутов и фоновых задач или пайплайнов.

Вы можете выставить webhook-роут:

POST /webhooks/stripe

А затем вынести медленную работу в задачу:

fulfill-order
send-notification
run-ai-classification
sync-customer

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

Когда допустима прямая обработка

Прямая обработка допустима, когда работа совсем маленькая и безопасная:

  • записать в лог внутреннее событие;
  • обновить лёгкий счётчик;
  • отправить уведомление по принципу fire-and-forget;
  • обработать некритичный внутренний webhook.

Но для платежей, провижининга, AI-workflow, цепочек внешних API и действий, обращённых к клиенту, — обрабатывайте позже.

Чеклист

Хороший webhook-обработчик должен отвечать на вопросы:

  • Может ли он ответить быстро?
  • Проверено ли событие?
  • Есть ли ID события для идемпотентности?
  • Вынесена ли медленная работа в задачу?
  • Можно ли безопасно повторить задачу?
  • Привязаны ли логи к ID события?
  • Можно ли игнорировать дубликаты событий?

Заключение

Webhook-обработчики должны быть скучными и быстрыми. Их задача — принять, проверить, записать и подтвердить.

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

Быстрое подтверждение уменьшает число дубликатов событий, повышает надёжность и упрощает эксплуатацию webhook-систем.