Подпись вебхуков
Проверяйте подпись входящих запросов с помощью секрета вебхука и обновляйте секрет при необходимости.
Основной раздел: Вебхуки.
Секрет и подпись
В блоке «Секрет вебхуков» хранится единый секрет подписи для всех вебхуков приложения, включая вызовы инструментов. Его можно показать или скрыть и скопировать в защищённое хранилище серверной части приложения. Ссылка «Подробнее» открывает окно с заголовками и формулой HMAC.
Если секрет потерян или скомпрометирован, нажмите «Заменить секрет». В окне подтверждения можно отменить действие или подтвердить замену. Старый секрет сразу перестанет работать для всех вебхуков и инструментов приложения, поэтому без паузы обновите его на бэкенде приложения.

Каждый запрос содержит заголовки:
Content-Type: application/json;X-App-Id— Client ID приложения;X-Webhook-Timestamp— время текущей попытки доставки, совпадающее сtimestampтела; при повторе оба значения меняются;X-Webhook-Event-Id— уникальный ID события, совпадающий сevent_idв теле;X-Webhook-Signature— hex-строка HMAC-SHA256.
Подпись защищает всё тело запроса. Она рассчитывается как HMAC-SHA256 от строки v1.timestamp.event_id.canonicalJson(payload) с секретом приложения. canonicalJson рекурсивно сортирует ключи объектов, сохраняет порядок массивов и не добавляет пробелы. В примере request.body содержит разобранный JSON полученного запроса:
import { createHmac, timingSafeEqual } from "node:crypto";
function canonicalize(value) {
if (Array.isArray(value)) return value.map(canonicalize);
if (value !== null && typeof value === "object") {
return Object.fromEntries(
Object.keys(value).sort().map((key) => [key, canonicalize(value[key])])
);
}
return value;
}
const payload = request.body;
const deliveryTimestamp = request.headers["x-webhook-timestamp"] ?? "";
const eventId = request.headers["x-webhook-event-id"] ?? "";
const signature = request.headers["x-webhook-signature"] ?? "";
const signedContent = [
"v1",
payload.timestamp,
payload.event_id,
JSON.stringify(canonicalize(payload)),
].join(".");
const expected = createHmac("sha256", webhookSecret)
.update(signedContent, "utf8")
.digest();
const received = Buffer.from(signature, "hex");
const signatureIsValid =
/^[a-f\d]{64}$/i.test(signature) &&
payload.timestamp === deliveryTimestamp &&
payload.event_id === eventId &&
received.length === expected.length &&
timingSafeEqual(received, expected);
Перед обработкой проверьте свежесть X-Webhook-Timestamp, подпись, соответствие X-Webhook-Event-Id полю event_id, X-App-Id, разрешённый project_id и тип события. Не записывайте секрет в логи и не передавайте его в чат. Повторный event_id не обрабатывайте заново.
Управление и замена секрета
В списке webhook показываются заданное название, URL, выбранные события, состояние, время последнего вызова и последний HTTP-статус. Через переключатель состояния webhook можно отключить и снова включить; отключённый webhook не получает новые боевые события.
Откройте страницу webhook, чтобы изменить название, URL или отмеченный галочками набор публичных событий, затем нажмите «Сохранить». Секрет не относится к отдельному URL: он доступен в общем блоке на странице вебхуков приложения.

Кнопка удаления webhook открывает подтверждение. После окончательного удаления отправка на этот URL прекращается; единый секрет приложения не меняется.