Pingveraблог ← Блог
Главная › Блог › Как отправлять события Pingvera в Helpdesk через webhook

Как отправлять события Pingvera в Helpdesk через webhook

4 августа 2026 · 21 мин чтения

Как отправлять события Pingvera в Helpdesk через webhook

Интеграция Pingvera с Helpdesk должна создавать одну управляемую карточку инцидента, обновлять её при изменении состояния и закрывать после подтверждённого восстановления. Она не должна создавать новый тикет на каждый повтор проверки.

Надёжный маршрут выглядит так:

Pingvera
   ↓ HTTPS webhook
входной адаптер
   ↓ проверка + сохранение
очередь событий
   ↓ нормализация + дедупликация
правила критичности и маршрутизации
   ↓ API
Helpdesk: создать / обновить / закрыть

Прямое соединение «webhook сразу в создание тикета» подходит только для простого прототипа. В production между системами нужен хотя бы слой, который умеет проверять запрос, переживать повторы и помнить связь между событием и заявкой.

Реальная схема webhook Pingvera

Pingvera отправляет вебхук POST-запросом, Content-Type: application/json, User-Agent: PingveraNotifier/1.0. Официальный payload:

{
  "event": "opened",
  "severity": "critical",
  "summary": "host offline",
  "incident": "inc_01J...",
  "org": "ООО Ромашка",
  "entity": "acme.com · Доступность",
  "target": "https://acme.com",
  "link": "https://app.pingvera.ru/#/incidents/inc_01J...",
  "started_at": "2026-07-18T10:15:00Z"
}

Поля:

  • event — opened | resolved | test | digest;
  • severity — critical | warning | info;
  • summary — краткое описание, может быть пустым;
  • incident — публичный ID инцидента вида inc_...; пустой для test и digest;
  • org — название организации-клиента;
  • entity — что именно проверяется (например, acme.com · Доступность);
  • target — цель проверки;
  • link — ссылка на карточку инцидента в дашборде;
  • started_at — время начала в RFC3339 UTC.

У payload нет отдельного delivery_id или event_id — единственный идентификатор в теле это incident (public ID), и он пустой для служебных типов test/digest. Это меняет то, как строить дедупликацию и incident key ниже: их придётся собирать из incident+event, а для доставок без incident — из хеша raw body.

Это официальная схема продукта на дату проверки статьи, а не реконструкция по одной тестовой доставке. Тем не менее ниже статья по-прежнему рекомендует прогнать реальную доставку через свой тестовый endpoint: схема может расшириться новыми необязательными полями (правило продукта — protobuf/API эволюционируют только добавлением полей, не ломающими изменениями), и адаптер должен переживать такие добавления, не считая их ошибкой.

Коротко

Порядок внедрения:

  1. определить, какие подтверждённые события должны создавать тикет;
  2. поднять собственный HTTPS endpoint;
  3. отправить из Pingvera тестовое событие и сохранить raw body с разрешёнными заголовками;
  4. выяснить фактическую аутентификацию, ID доставки, типы и состояния;
  5. описать внутреннюю нормализованную схему;
  6. сохранять входящее событие до ответа 2xx;
  7. дедуплицировать повторные доставки;
  8. связать один инцидент с одним тикетом;
  9. обрабатывать создание, обновление и восстановление как переходы состояния;
  10. мониторить сам адаптер, очередь и ошибки Helpdesk.

Для P1 webhook не должен быть единственным каналом. Сбой адаптера или Helpdesk не должен лишать дежурного резервного уведомления.

Сначала определите бизнес-правило

Не каждое наблюдение должно становиться заявкой.

Пример таблицы:

Событие Действие Helpdesk Срочное уведомление
Единичная неудачная проверка Сохранить для подтверждения, тикет не создавать Нет
Подтверждённая потеря критической функции Создать инцидент По маршруту P1/P2
Повтор того же активного сбоя Обновить существующую карточку по правилам Только при эскалации
Изменение влияния Обновить приоритет и комментарий Если стало P1
Восстановление одной из зависимых проверок Обновить контекст Обычно нет
Подтверждённое восстановление функции Перевести в наблюдение или решить По регламенту
Плановое окно Не создавать либо пометить как maintenance Нет
Истекающий домен/SSL Создать плановую задачу По сроку, не как падение
Нет heartbeat Создать задачу после допустимого окна По критичности процесса

Критичность определяется не одним HTTP-кодом. Нужны:

  • класс проекта;
  • нарушенная бизнес-функция;
  • время и зона влияния;
  • наличие обходного пути;
  • договорные правила;
  • подтверждение;
  • текущий режим работ.

Используйте общую шкалу P1–P4, чтобы приоритет в Pingvera, Helpdesk и регламенте означал одно и то же.

Почему нужен адаптер

Разные форматы

Мониторинг сообщает о проверке и состоянии. Helpdesk ожидает тему, описание, проект, очередь, приоритет и исполнителя.

Разная модель жизненного цикла

Webhook может приходить для каждого изменения. Helpdesk хранит одну карточку инцидента с комментариями и статусами.

Повторные доставки

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

Порядок не гарантирован как архитектурное предположение

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

Временная недоступность Helpdesk

Входное событие нельзя терять только потому, что API тикетов ответил 503.

Безопасность

Публичный endpoint нужно защищать от поддельных запросов, replay, слишком больших payload и утечки секретов.

Компоненты минимальной архитектуры

1. HTTPS ingress

Принимает только ожидаемый метод и content type. Ограничивает размер тела, сохраняет raw body для проверки подписи и не выполняет долгую работу в запросе.

2. Проверка источника

Pingvera поддерживает HMAC-подпись: если у webhook-канала задан секрет, к запросу добавляются заголовки X-Pingvera-Signature: sha256=<hex> (HMAC-SHA256 от секрета и raw body) и X-Pingvera-Timestamp: <unix-секунды> (информационный, в саму подпись не входит). Секрет с префиксом whsec_ показывается один раз — в ответе POST /api/v1/channels при создании канала — и больше нигде не отдаётся.

Каналы, созданные до появления подписи, приходят без этих двух заголовков — доставка при этом не ломается, но и проверить источник по подписи для них нельзя. Если у канала подписи нет, изучите доступные варианты:

  • секретный заголовок;
  • Basic/Bearer-аутентификация;
  • уникальный endpoint;
  • mTLS;
  • IP allowlist из официально публикуемого диапазона;
  • приём через проверенный интеграционный шлюз.

Уникальный URL-токен может попасть в журналы и историю. Не помещайте в query string другие секреты. IP allowlist полезен только как дополнительная мера и требует обновления.

3. Inbox

До ответа источнику событие записывается в устойчивое хранилище:

  • delivery_id;
  • время получения;
  • хеш raw body;
  • тип;
  • статус обработки;
  • безопасно отфильтрованный payload или его защищённая ссылка;
  • число попыток;
  • последняя ошибка.

После успешной записи можно ответить 202 Accepted или другим документированным успешным кодом.

4. Очередь и worker

Worker независимо:

  • нормализует;
  • проверяет схему;
  • определяет проект;
  • находит incident key;
  • создаёт или обновляет тикет;
  • повторяет временную ошибку;
  • отправляет неразбираемое событие в dead-letter queue.

5. Таблица соответствия

Хранит:

incident_key → helpdesk_ticket_id → last_source_state → last_event_time

Без неё интеграция создаёт дубликаты.

Проверьте фактический payload на своём аккаунте

Официальная схема приведена выше. Реальную тестовую доставку всё равно стоит записать — не чтобы угадать поля, а чтобы увидеть, как заполнены org/entity/summary для конкретных типов проверок в вашем аккаунте, и убедиться, что адаптер корректно обрабатывает test/digest с пустым incident.

Не используйте публичный request-bin для клиентских production-событий: payload может содержать URL, названия проектов и операционные детали.

Поднимите контролируемый endpoint в тестовом контуре.

Он должен временно сохранить:

  • HTTP method;
  • content type;
  • разрешённые заголовки;
  • raw body;
  • время;
  • статус ответа.

Перед тестом:

  • создайте отдельный тестовый сайт или проверку;
  • используйте нейтральное название;
  • исключите секреты и персональные данные;
  • ограничьте доступ к журналу;
  • установите срок удаления raw payload;
  • не выводите заголовок авторизации в общий лог.

Отправьте минимум три состояния, если интерфейс позволяет:

  1. подтверждённая проблема;
  2. повтор или обновление;
  3. восстановление.

Сравните с описанной схемой:

  • заполнен ли incident (ожидаемо — пустой только для test/digest);
  • как выглядит entity для разных типов проверки (http/tcp/dns/tls/wp/domain/links/heartbeat);
  • пустой ли summary в вашем случае;
  • пришли ли заголовки X-Pingvera-Signature/X-Pingvera-Timestamp (зависит от того, задан ли секрет у канала);
  • что происходит при 500 и таймауте на вашей стороне.

Поведение retry (10 секунд на ответ, до 8 попыток с exponential backoff) проверяйте безопасным тестом, не вызывая искусственную ошибку для реального P1.

Внутренний нормализованный контракт

После исследования преобразуйте любой входящий формат в стабильную схему студии.

{
  "schema_version": "studio.incident-event/v1",
  "source": "pingvera",
  "delivery_id": "delivery-or-derived-id",
  "event_id": "source-event-id",
  "event_type": "check.failed_confirmed",
  "occurred_at": "2026-08-04T12:10:00Z",
  "received_at": "2026-08-04T12:10:02Z",
  "project_id": "WEB-0042",
  "check_id": "source-check-id",
  "check_key": "lead-form",
  "incident_key": "WEB-0042:lead-form:source-incident-id",
  "state": "open",
  "severity_hint": "P2",
  "summary": "Не подтверждается доставка тестовой заявки",
  "target": "https://example.ru/contact/",
  "observations": {
    "regions_failed": 2,
    "last_status": 200,
    "reason_code": "delivery_not_confirmed"
  },
  "details_url": "https://app.pingvera.ru/..."
}

Это пример внутреннего контракта студии, а не зеркало payload Pingvera. event_id/delivery_id в схеме Pingvera нет — заполняйте delivery_id производным значением (например, хешем raw body или incident:event:started_at), а event_id — из поля event. project_id/check_id/check_key — сопоставление по вашему каталогу проектов, incident_key собирается из incident, как описано выше, state/severity_hint — из event/severity реального payload.

Обязательные поля

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

Не переносите автоматически

  • секреты;
  • полные HTTP-заголовки проверяемого сайта;
  • cookie;
  • содержимое форм с персональными данными;
  • токены в URL;
  • большой HTML-ответ;
  • внутренний stack trace без фильтрации;
  • пароль тестового пользователя.

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

Как построить incident key

Лучший вариант — стабильный ID инцидента от источника. У Pingvera это поле incident (inc_...) в payload opened/resolved:

pingvera:<incident>

Для событий test и digest поле incident пустое — на них incident key не строится, это не бизнес-инцидент. Если по какой-то причине incident недоступен, используйте контролируемую корреляцию:

<project_id>:<check_key>:<opened_period>

Нельзя использовать только название проверки: оно может измениться и повторяться.

Правила:

  • один активный инцидент по ключу;
  • повтор открытого состояния обновляет карточку;
  • восстановление закрывает только соответствующий активный инцидент;
  • новое нарушение после закрытия получает новый occurrence ID;
  • зависимые проверки могут группироваться общим incident key только по явному правилу;
  • массовый сбой провайдера может иметь parent incident.

Дедупликация

Сделайте уникальный индекс по source + delivery_id.

Псевдокод:

if inbox.contains(source, delivery_id):
    return 2xx

persist(raw_event, status="received")
return 202

В worker:

event = normalize(raw_event)

if processed_events.contains(event.event_id, event.event_type):
    mark_duplicate()
    stop

apply_state_transition(event)
mark_processed()

Почему два уровня:

  • одна доставка может прийти повторно;
  • два разных delivery ID могут описывать одно логическое событие;
  • источник может вручную переотправить событие;
  • Helpdesk может принять запрос, а адаптер потерять ответ.

Stripe в своей документации прямо рекомендует хранить обработанные event ID и игнорировать повтор с успешным ответом. Это общий паттерн, а не описание конкретного поведения Pingvera.

Машина состояний

Не обновляйте тикет произвольным текстом. Определите допустимые переходы.

unknown
   ↓ confirmed failure
open
   ↓ repeated failure
open + observation
   ↓ partial recovery
monitoring
   ↓ confirmed recovery
resolved
   ↓ new confirmed failure
new occurrence

Событие с более старым occurred_at не должно возвращать карточку из resolved в open без отдельной логики.

Храните:

  • последнее принятое время источника;
  • версию или последовательность, если есть;
  • время получения;
  • текущий тикет;
  • состояние;
  • историю отклонённых переходов.

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

Маппинг в Helpdesk

Создание

Поле тикета Источник
Тема Класс + проект + нарушенная функция
Описание Влияние, время, подтверждение, URL, наблюдения
Очередь Владелец проекта или тип услуги
Приоритет Правила P1–P4, не только severity hint
Клиент project_id из каталога
Теги pingvera, тип проверки, класс проекта
Внешний ID incident_key
Исполнитель Дежурная роль
Ссылка Карточка Pingvera/проекта

Пример темы:

[P2][WEB-0042] Не подтверждается доставка заявки с формы

Пример описания:

Pingvera подтвердила нарушение критической функции.

- Проект: WEB-0042 — Магазин «Север»
- Функция: доставка заявки с формы
- Начало по источнику: 15:10 МСК
- Подтверждение: 2 повторные проверки
- Пользовательское влияние: тестовая заявка не обнаружена в целевом ящике
- Состояние: открыто
- Паспорт проекта: [внутренняя ссылка]
- Подробности мониторинга: [защищённая ссылка]

Первый шаг: выполнить runbook `lead-delivery-v2`.

Не пишите «клиент теряет миллионы», если мониторинг этого не измеряет.

Обновление

Добавляйте комментарий только при значимом изменении:

  • новый регион;
  • рост влияния;
  • эскалация;
  • смена причины-кандидата;
  • действие инженера;
  • частичное восстановление;
  • подтверждённый recovery.

Не добавляйте комментарий каждую минуту «по-прежнему не работает» без полезного контекста. Счётчик повторов можно обновлять агрегированно.

Решение

После recovery не всегда нужно сразу закрывать заявку.

Рабочая последовательность:

source recovered → статус «Наблюдение» → контрольные проверки → «Решено»

В комментарии:

  • время первого успешного результата;
  • число подтверждений;
  • что проверено;
  • остаётся ли влияние;
  • нужен ли отчёт или postmortem.

Маппинг критичности

Пример правил:

rules:
  - when:
      service_class: A
      check_key: checkout
      state: open
    priority: P1
    queue: oncall

  - when:
      service_class: B
      check_type: synthetic_form
      state: open
    priority: P2
    queue: support

  - when:
      event_type: certificate.expiring
      days_left_gte: 14
    priority: P3
    queue: planned-work

Поля и DSL условны. Важно хранить правила отдельно от кода транспорта и покрывать их тестами.

Проверочные сценарии:

  • сайт A, checkout недоступен → P1;
  • сайт C, главная кратко недоступна → P2/P3 по договору;
  • SSL 20 дней → плановая задача;
  • плановые работы → suppression;
  • повтор активного P1 → обновление, не новый тикет.

Безопасность входящего webhook

HTTPS

Используйте действующий сертификат и не отключайте его проверку.

Подпись

Если у webhook-канала Pingvera задан секрет (whsec_..., выдаётся один раз при создании канала), запрос несёт X-Pingvera-Signature: sha256=<hex> и X-Pingvera-Timestamp: <unix-секунды>:

  1. считайте raw body до JSON parsing — подпись считается ровно от тех байт, что пришли в теле;
  2. возьмите X-Pingvera-Signature, отрежьте префикс sha256=;
  3. вычислите HMAC-SHA256(secret, raw_body) и сравните с полученным значением;
  4. сравнивайте constant-time функцией (hmac.Equal/hash_equals/hmac.compare_digest), не строковым ==;
  5. X-Pingvera-Timestamp в саму подпись не входит — это информационное поле; если вам нужно окно свежести против replay, определите его сами на основе этого заголовка, так как Pingvera такое окно не документирует;
  6. храните секрет в vault;
  7. учитывайте, что каналы, созданные до появления подписи, придут вовсе без этих двух заголовков — это ожидаемое поведение, а не ошибка доставки, и адаптер должен явно решить, что делать с такими каналами (принимать без проверки подписи или требовать пересоздания канала с секретом).

Поскольку secret отдаётся только один раз при создании канала, конкретный порядок ротации (пересоздание канала целиком или отдельная операция смены секрета) нужно уточнить в актуальном интерфейсе Pingvera — статья этого не утверждает.

Размер и формат

  • разрешить только POST;
  • ограничить body, например внутренним разумным лимитом;
  • проверять Content-Type;
  • отклонять невалидный JSON;
  • ограничить глубину и неизвестные поля по схеме;
  • не распаковывать произвольные архивы.

Защита от replay

  • уникальный индекс по производному ключу доставки (у Pingvera нет отдельного delivery ID в теле — используйте хеш raw body или incident+event+started_at);
  • X-Pingvera-Timestamp как справочный сигнал для собственного допустимого окна, если вы его вводите;
  • журнал повторов;
  • успешный ответ уже обработанному событию;
  • запрет повторного необратимого действия.

SSRF и произвольные ссылки

Не заставляйте worker автоматически загружать любой target или details_url из payload. Если нужны дополнительные данные, используйте заранее разрешённый API hostname и собственный идентификатор.

Секрет Helpdesk

Исходящий API-токен:

  • имеет минимальные права на нужную очередь;
  • не может администрировать пользователей;
  • хранится в vault;
  • не выводится в лог;
  • ротируется;
  • отделён для production и теста.

Проверка HMAC для Pingvera на Node.js

Фрагмент реализует фактический контракт Pingvera: заголовок X-Pingvera-Signature: sha256=<hex>, алгоритм HMAC-SHA256 от секрета и raw body. Если у конкретного канала секрета нет (старый канал, созданный до появления подписи), заголовка не будет — это ожидаемо, а не ошибка.

import crypto from "node:crypto";

function safeEqual(a, b) {
  const left = Buffer.from(a, "utf8");
  const right = Buffer.from(b, "utf8");
  if (left.length !== right.length) return false;
  return crypto.timingSafeEqual(left, right);
}

export function verifyPingveraSignature({ rawBody, signatureHeader, secret }) {
  if (!signatureHeader || !secret) return false;

  const [scheme, hex] = signatureHeader.split("=");
  if (scheme !== "sha256" || !hex) return false;

  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  return safeEqual(hex, expected);
}

Проверка выполняется по неизменённому raw body. Если middleware сначала разобрал JSON, а потом сериализовал его заново, подпись может не совпасть.

Псевдокод входного обработчика

async function receiveWebhook(request, response) {
  const rawBody = await readRawBody(request, { maxBytes: 256_000 });

  if (!isExpectedContentType(request.headers["content-type"])) {
    return response.status(415).end();
  }

  if (!verifyPingveraSignature({
    rawBody,
    signatureHeader: request.headers["x-pingvera-signature"],
    secret: channelSecret
  })) {
    return response.status(401).end();
  }

  const payload = parseJson(rawBody);
  // У payload Pingvera нет отдельного delivery ID — берём производный ключ:
  // incident (пустой для test/digest) + event + started_at, либо хеш raw body.
  const deliveryId = payload.incident
    ? `${payload.incident}:${payload.event}:${payload.started_at}`
    : sha256(rawBody);

  const inserted = await inbox.insertIfAbsent({
    source: "pingvera",
    deliveryId,
    receivedAt: new Date().toISOString(),
    rawBodyEncrypted: encryptForInbox(rawBody),
    bodyHash: sha256(rawBody),
    status: "received"
  });

  // Повтор уже принят: вернуть успех и не создавать вторую работу.
  if (!inserted) return response.status(200).end();

  await queue.publish({ source: "pingvera", deliveryId });
  return response.status(202).end();
}

Функции хранения, шифрования, очереди и проверки контракта нужно реализовать выбранными технологиями. In-memory Set не подходит для production: после перезапуска дедупликация исчезнет.

Worker и вызов Helpdesk

async function processDelivery(message) {
  const raw = await inbox.get(message.source, message.deliveryId);
  const sourcePayload = JSON.parse(decrypt(raw.rawBodyEncrypted));

  // event: "opened" | "resolved" | "test" | "digest" (реальные значения Pingvera).
  if (sourcePayload.event === "test" || sourcePayload.event === "digest") {
    return inbox.markProcessed(message.deliveryId); // не бизнес-инцидент — тикет не создаём.
  }

  const event = normalizePingveraPayload(sourcePayload);

  validateInternalSchema(event);

  if (await processed.exists(event.source, event.eventId, event.eventType)) {
    return inbox.markDuplicate(message.deliveryId);
  }

  const current = await incidents.findActive(event.incidentKey);

  if (event.state === "open" && !current) {
    const ticket = await helpdesk.create(buildTicket(event), {
      idempotencyKey: event.incidentKey
    });
    await incidents.bind(event.incidentKey, ticket.id, event);
  } else if (event.state === "open" && current) {
    await helpdesk.addMeaningfulUpdate(current.ticketId, event);
    await incidents.advance(current, event);
  } else if (event.state === "resolved" && current) {
    await helpdesk.moveToMonitoringOrResolve(current.ticketId, event);
    await incidents.advance(current, event);
  } else {
    await deadLetter.store(event, "unexpected_state_transition");
  }

  await processed.mark(event);
  await inbox.markProcessed(message.deliveryId);
}

idempotencyKey сработает только если конкретный Helpdesk поддерживает такой механизм. Иначе используйте внешний ID, поиск перед созданием и собственную таблицу соответствия.

Универсальный шаблон запроса в Helpdesk

Endpoint и поля условны.

POST /api/tickets HTTP/1.1
Host: helpdesk.example.ru
Authorization: Bearer <token-from-vault>
Content-Type: application/json
Idempotency-Key: pingvera:WEB-0042:lead-form:incident-123

{
  "queue": "website-support",
  "priority": "P2",
  "subject": "[P2][WEB-0042] Не доставляется тестовая заявка",
  "description": "...",
  "external_id": "pingvera:WEB-0042:lead-form:incident-123",
  "tags": ["pingvera", "synthetic-form", "WEB-0042"]
}

Проверьте официальную документацию вашего Helpdesk:

  • метод создания;
  • формат авторизации;
  • ограничения полей;
  • idempotency;
  • поиск по внешнему ID;
  • rate limits;
  • retry-after;
  • статусы;
  • добавление комментария;
  • закрытие;
  • sandbox.

Не выводите токен в командную строку общего CI, если он попадёт в историю или список процессов.

Вариант через n8n

Для небольшой студии адаптер можно собрать в n8n, если self-hosted или выбранный контур соответствует требованиям к данным и доступам.

Рабочий workflow:

Webhook Trigger
   ↓
проверка source authentication
   ↓
сохранение delivery ID в Data Store / БД
   ↓
Respond to Webhook: 202
   ↓
нормализация полей
   ↓
загрузка project_id из каталога
   ↓
поиск incident_key → ticket_id
   ↓
Switch: create / update / resolve / ignore
   ↓
HTTP Request в Helpdesk
   ↓
запись результата и метрик

Особые проверки:

  • доступен ли raw body для HMAC до parsing;
  • сохраняется ли workflow execution с секретами;
  • маскируются ли credentials;
  • переживает ли Data Store restart;
  • что происходит при повторе;
  • как работает error workflow;
  • кто может редактировать production workflow;
  • можно ли экспортировать и версионировать схему;
  • есть ли отдельный тестовый webhook.

Если raw body недоступен или изменяется, HMAC-проверку лучше выполнить на reverse proxy/функции перед n8n.

Повторы и ошибки

Временные ошибки

Повторять с ограниченным exponential backoff и jitter:

  • timeout;
  • 429 с учётом Retry-After;
  • 502, 503, 504;
  • временный сетевой сбой.

Постоянные ошибки

Не повторять бесконечно:

  • невалидная схема;
  • неизвестный project ID;
  • запрещённый тип;
  • 401/403 до исправления credentials;
  • удалённая очередь;
  • недопустимое состояние.

Отправлять в dead-letter queue с уведомлением владельцу.

Частичный успех

Helpdesk мог создать тикет, но ответ потерялся. Перед новой попыткой ищите по external ID или используйте idempotency key. Иначе появятся дубликаты.

Что возвращать Pingvera

  • 2xx — событие надёжно принято или уже было принято;
  • 4xx — запрос не пройдёт без изменения;
  • 5xx — временно не удалось сохранить приём.

Pingvera ждёт ответ 10 секунд; любой статус ≥300 или таймаут считается неудачей. Неудачи повторяются с exponential backoff до 8 попыток общей продолжительностью около 2 часов 15 минут, после чего доставка помечается окончательно неудавшейся — дальше повторов не будет, и это единственный источник события до следующего изменения состояния. Отсюда следует, что 2xx нужно возвращать быстро (входной обработчик не должен ждать вызова Helpdesk синхронно) и не возвращать 2xx, если событие потеряно до устойчивого хранения — иначе оно не переотправится.

Наблюдаемость самого адаптера

Интеграция мониторинга тоже может сломаться.

Контролируйте:

  • время последней принятой доставки;
  • число 2xx/4xx/5xx;
  • ошибки подписи;
  • размер очереди;
  • возраст самого старого события;
  • retry;
  • dead-letter;
  • задержку до создания тикета;
  • дубликаты;
  • неизвестные проекты;
  • ошибки API Helpdesk;
  • последнюю успешную тестовую доставку.

Создайте синтетический тест:

  1. контролируемое тестовое событие;
  2. запись в inbox;
  3. создание тестовой карточки;
  4. обновление;
  5. решение;
  6. проверка метрики;
  7. автоматическая очистка тестовых данных.

Не запускайте такой тест в боевую очередь P1 без специальной маркировки.

Runbook интеграции

# Runbook: Pingvera → Helpdesk

## Владелец
- Основной:
- Резерв:
- Канал:

## Компоненты
- Webhook URL:
- Inbox:
- Queue:
- Worker:
- Helpdesk project/queue:
- Vault refs:

## Проверка здоровья
1. Проверить ingress 2xx.
2. Проверить возраст очереди.
3. Проверить dead-letter.
4. Проверить Helpdesk API.
5. Найти delivery ID.

## Повторная обработка
- Кто одобряет:
- Команда/интерфейс:
- Как проверить дедупликацию:
- Как не создать второй тикет:

## Ротация
- Секрет входящего webhook:
- Токен Helpdesk:
- Порядок двухключевого периода:

## Аварийный режим
- Резервный канал P1:
- Ручное создание тикета:
- Как пометить пропущенные события:

## Контрольный тест
- Последний результат:
- Следующая дата:

Чек-лист перед production

Контракт

  • [ ] Сохранены тестовые payload состояний failure/update/recovery.
  • [ ] Имена полей и заголовков взяты из фактической доставки.
  • [ ] Зафиксирована версия внутренней схемы.
  • [ ] Неизвестные типы уходят в quarantine.

Надёжность

  • [ ] Событие сохраняется до 2xx.
  • [ ] Есть persistent deduplication.
  • [ ] Один инцидент связан с одним тикетом.
  • [ ] Повтор безопасен.
  • [ ] Неупорядоченное событие не ломает состояние.
  • [ ] Есть очередь и dead-letter.
  • [ ] Частичный успех Helpdesk обработан.

Безопасность

  • [ ] HTTPS включён.
  • [ ] Проверяется документированный механизм источника.
  • [ ] Секреты находятся в vault.
  • [ ] Raw body ограничен по размеру и сроку хранения.
  • [ ] Логи очищены от credentials и персональных данных.
  • [ ] Helpdesk token имеет минимальные права.
  • [ ] Ссылки из payload не загружаются произвольно.
  • [ ] Есть ротация и offboarding.

Операции

  • [ ] P1 имеет резервный канал.
  • [ ] Назначены владелец и резерв адаптера.
  • [ ] Метрики и алерт интеграции настроены.
  • [ ] Runbook проверен другим инженером.
  • [ ] Тест create/update/resolve пройден.
  • [ ] Клиентские сообщения не отправляются автоматически без правила.

Частые ошибки

Новый тикет на каждый алерт

Очередь заполняется дубликатами. Нужны incident key и таблица соответствия.

2xx до сохранения

Процесс падает, событие теряется, источник считает доставку успешной. Сначала persistent inbox.

Долгая обработка внутри webhook

Helpdesk тормозит, отправитель получает timeout и повторяет событие. Отвечайте после приёма, обрабатывайте асинхронно.

Доверие любому POST

Публичный endpoint создаёт поддельные P1. Проверяйте доступный документированный механизм источника.

Секрет в URL

Он попадает в access logs и историю. Используйте безопасный механизм, если продукт его поддерживает, и защищайте сам URL.

Повтор закрывает новый инцидент

Старое recovery приходит после нового failure. Учитывайте occurrence, время и допустимые переходы.

В тикет копируется весь payload

Туда попадают секреты, HTML и чувствительные данные. Используйте нормализованное безопасное описание.

Нет мониторинга интеграции

Вебхук перестал создавать тикеты, но команда узнаёт об этом на реальном сбое. Нужен контрольный тест.

Как помогает Pingvera

Pingvera остаётся источником фактов о наблюдаемой функции:

  • момент подтверждённого нарушения;
  • объект и тип проверки;
  • повторные результаты;
  • восстановление;
  • ссылка на историю;
  • доставка события в webhook как один из каналов.

Helpdesk становится системой работы:

  • владелец;
  • статус;
  • действия;
  • клиентская коммуникация;
  • сроки;
  • итог.

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

Начните с безопасного теста: создайте отдельный webhook-канал в Pingvera, запишите фактический payload и проведите событие через тестовую очередь Helpdesk до включения production-маршрута.

Часто задаваемые вопросы

Можно ли направить webhook напрямую в Helpdesk?

Да, если Helpdesk принимает точный формат, умеет аутентифицировать источник, дедуплицировать и сопоставлять состояния. На практике небольшой адаптер или n8n-flow обычно нужен для нормализации и связи тикетов.

Какой payload отправляет Pingvera?

POST с Content-Type: application/json, полями event (opened/resolved/test/digest), severity (critical/warning/info), summary, incident (inc_..., пустой для test/digest), org, entity, target, link и started_at (RFC3339 UTC). Полная схема — в разделе «Реальная схема webhook Pingvera» выше. Внутренний нормализованный контракт студии — это уже следующий шаг, отдельный от формата Pingvera.

Нужна ли база данных?

Для production нужна устойчиво хранимая связь delivery/event/incident/ticket. Это может быть реляционная БД, надёжный key-value store или возможности интеграционной платформы. Память процесса недостаточна.

Что делать, если webhook не подписывается?

Pingvera поддерживает подпись (X-Pingvera-Signature), но только для каналов, у которых задан секрет: у каналов, созданных до появления этой функции, заголовков не будет. Если ваш канал реально без подписи, используйте наиболее сильный доступный механизм: секретный заголовок, авторизацию, mTLS, allowlist или защищённый gateway — и рассмотрите пересоздание канала с секретом, если это не критично для существующих интеграций.

Когда отвечать 2xx?

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

Как не создавать повторные заявки?

Хранить delivery ID, event ID и incident key, а также связь с ticket ID. При повторе возвращать успех и обновлять существующую карточку только при новом значимом состоянии.

Нужно ли автоматически закрывать тикет?

Для простого события можно перевести его в «Наблюдение», а закрыть после нескольких успешных проверок или подтверждения инженера. Правило зависит от критичности и процесса.

Можно ли использовать n8n?

Да, если вы проверили raw body, аутентификацию, устойчивое хранение, error workflow, секреты и доступы редакторов. Для критичного потока одного визуального happy path недостаточно.

Главное

Webhook — это транспорт события, а не готовый процесс инцидента.

Надёжная интеграция строится так:

проверить → сохранить → подтвердить приём → нормализовать → дедуплицировать → сопоставить → изменить тикет → наблюдать

Если начать с этого каркаса, повторная доставка не создаст десять заявок, недоступность Helpdesk не потеряет P1, а восстановление обновит правильную карточку.

Источники и дополнительное чтение

  • GitHub Docs: Best practices for using webhooks
  • GitHub Docs: Validating webhook deliveries
  • Stripe Docs: Receive events in a webhook endpoint
  • Stripe Docs: Process undelivered webhook events
  • OWASP: Secrets Management Cheat Sheet
  • OWASP: Logging Cheat Sheet
  • Pingvera: шкала критичности инцидентов
  • Pingvera: регламент действий при падении сайта
  • Pingvera: мониторинг как код

Следующая учебная ступень: «Telegram, email или MAX: как не зависеть от одного канала уведомлений».

Узнавайте о проблеме раньше клиента

Pingvera следит за сайтами клиентов «снаружи и изнутри» — доступность из регионов, формы, оплата, домен, SSL, сервер — и предупреждает в Telegram, email и Max раньше, чем напишет клиент.

Попробовать Pingvera бесплатно

Читайте также: Terraform-провайдер Pingvera: мониторинг как код · CLI Pingvera: мониторинг из терминала и скриптов · Как перенести статус-страницу на Pingvera за 10 минут · Ping-Admin или Pingvera: что выбрать студии и владельцу сайта · Бесплатно проверить сайт.

← Все статьи · Политика конфиденциальности · pingvera.ru · Telegram-канал

На сайте осуществляется обработка пользовательских данных с использованием Cookie в соответствии с Политикой конфиденциальности.