Перейти к содержанию

Получение уведомлений

При наступлении события Pert отправляет HTTP POST на URL вашего webhook. Тело запроса одинаково для всех подписчиков одного и того же события — рабочее пространство передаётся отдельным заголовком, что позволяет переиспользовать одну подпись для всей рассылки.

HTTP-заголовки

Заголовок Описание
Content-Type Всегда application/json
X-Webhook-Id UUID вашего webhook
X-Webhook-Event-Id UUID события — используйте как ключ идемпотентности
X-Webhook-Timestamp Unix-время отправки в миллисекундах
X-Webhook-Workspace-Id UUID рабочего пространства, в котором произошло событие
X-Webhook-Signature Подпись тела запроса (base64). См. Верификация подписи

Workspace в заголовке, а не в теле

Тело запроса идентично для всех подписчиков одного события. Это позволяет вычислить подпись один раз и использовать её при доставке всем webhook — снижает нагрузку на KMS и ускоряет fan-out. Если вам нужно различать события по рабочим пространствам, читайте X-Webhook-Workspace-Id.

Тело запроса

{
  "event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "event_type": "transaction.created",
  "category": "Transactions",
  "timestamp": "2026-04-23T10:15:30Z",
  "data": {
    "...": "данные события"
  }
}
Поле Тип Описание
event_id UUID Уникальный идентификатор события. Совпадает с заголовком X-Webhook-Event-Id
event_type string Тип события (например, transaction.created)
category string Категория, по которой ваш webhook подписан
timestamp string ISO 8601, время наступления события
data object Полезная нагрузка события — структура зависит от event_type

Структура data описана для каждого event_type отдельно — см. Типы событий.

Ответ вашего сервера

Ваш сервер должен вернуть HTTP-код 2xx (например, 200 OK) в течение 10 секунд.

Условие Что произойдёт
Код 2xx за ≤ 10 секунд Доставка считается успешной, статус записи в истории — success
Код не 2xx Доставка считается ошибкой, запланирован retry (см. Повторные попытки)
Таймаут (> 10 секунд) Соединение разрывается, доставка считается ошибкой, запланирован retry
Ошибка TCP / DNS / TLS Доставка считается ошибкой, запланирован retry

Тело ответа не используется логикой доставки. Pert сохраняет первые 4 КБ тела ответа в истории доставок — чтобы вам было проще диагностировать проблемы.

Не блокируйте ответ обработкой

Не выполняйте долгую обработку синхронно. Сохраните payload в очередь или БД и сразу возвращайте 200 OK — затем обрабатывайте событие в фоне. Это исключает таймауты и ненужные retry, когда ваш сервер на самом деле получил событие.

Пример обработчика

from flask import Flask, request, abort

app = Flask(__name__)

@app.post("/webhook")
def webhook():
    # 1. Сначала проверьте подпись над сырым телом
    body = request.get_data()
    signature = request.headers.get("X-Webhook-Signature", "")
    if not verify_webhook(body, signature, PUBLIC_KEY_B64):
        abort(401)

    # 2. Только теперь парсите JSON и кладите в очередь
    event = request.get_json()
    queue.publish(event)

    # 3. Возвращайте 200 как можно быстрее
    return "", 200
import express from "express";

const app = express();

// ВАЖНО: raw-тело нужно для верификации подписи
app.use("/webhook", express.raw({ type: "application/json" }));

app.post("/webhook", (req, res) => {
  const signature = req.headers["x-webhook-signature"];
  if (!verifyWebhook(req.body, signature, PUBLIC_KEY_B64)) {
    return res.sendStatus(401);
  }
  const event = JSON.parse(req.body.toString("utf8"));
  queue.publish(event);
  res.sendStatus(200);
});
func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
    if err != nil {
        http.Error(w, "read", http.StatusBadRequest)
        return
    }
    sig := r.Header.Get("X-Webhook-Signature")
    if !verifyWebhook(body, sig, publicKeyB64) {
        http.Error(w, "invalid signature", http.StatusUnauthorized)
        return
    }
    var event WebhookPayload
    if err := json.Unmarshal(body, &event); err != nil {
        http.Error(w, "json", http.StatusBadRequest)
        return
    }
    queue.Publish(event)
    w.WriteHeader(http.StatusOK)
}

См. полные примеры верификации подписи на странице Верификация подписи.