SkyStream API

Вебхуки

Как получать события на свой адрес и проверять, что их отправили мы.

Вебхук — запрос POST на ваш адрес с событием, отправленный сразу, как оно случилось. Вебхуки избавляют от опроса API в ожидании перемен.

Адрес

Адреса заводятся в панели, раздел «API» → «Вебхуки»: до 5 адресов на аккаунт. У адреса задаются:

  • адрес — только https, доступный из интернета, на порту 443 или 8443. Адреса во внутренних сетях не принимаются;
  • события — все или выбранные типы. «Все» включает и типы, которые появятся позже;
  • описание — для себя.

Заведение адреса и смена его подтверждаются кодом из письма; о заведении приходит уведомление. Секрет подписи показывается один раз — при заведении. Сохраните его сразу.

Запрос

POST /webhooks HTTP/1.1
Content-Type: application/json
webhook-id: evt_1024
webhook-timestamp: 1790769600
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{"id":"evt_1024","type":"run.stopped","object_type":"run","created_at":"2026-09-30T12:00:00.000Z","data":{"object":{...}}}
ЗаголовокЧто содержит
webhook-idИдентификатор события — тот же, что id в теле. У повтора он не меняется
webhook-timestampВремя отправки в секундах Unix
webhook-signatureПодпись: v1, и HMAC-SHA256 в base64. Подписей бывает две через пробел — после смены секрета

Тело — событие в том же виде, что возвращает GET /events/{id}.

Ответ

Ответьте кодом 2xx в течение 10 секунд — тогда событие доставлено. Любой другой код, тайм-аут или обрыв соединения — неудача. Перенаправления (3xx) не выполняются и тоже считаются неудачей.

Отвечайте сразу, а событие обрабатывайте после ответа: долгая обработка внутри запроса упрётся в тайм-аут, и событие придёт повторно.

Проверка подписи

Подпись — по открытому стандарту Standard Webhooks. Проверять её удобнее готовой библиотекой: они есть для JavaScript, Python, Go, PHP, Java, Ruby и других языков.

import { Webhook } from "standardwebhooks";

const webhook = new Webhook(process.env.WEBHOOK_SECRET); // whsec_…

app.post("/webhooks", express.raw({ type: "application/json" }), (req, res) => {
  let event;
  try {
    event = webhook.verify(req.body, req.headers);
  } catch {
    return res.sendStatus(400);
  }
  res.sendStatus(204);
  handle(event); // после ответа
});

Без библиотеки подпись проверяется так:

  1. Возьмите тело запроса как пришло, байтами: разобранный и снова собранный JSON даст другую строку.
  2. Соберите строку {webhook-id}.{webhook-timestamp}.{тело}.
  3. Посчитайте HMAC-SHA256 от неё. Ключ — часть секрета после whsec_, раскодированная из base64.
  4. Сравните v1,<base64 результата> с каждой подписью из webhook-signature сравнением постоянного времени.
  5. Отклоните запрос, если webhook-timestamp отличается от текущего времени больше чем на 5 минут: так перехваченный запрос нельзя отправить повторно.
import crypto from "node:crypto";

function verify(secret, headers, rawBody) {
  const id = headers["webhook-id"];
  const timestamp = headers["webhook-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const expected = crypto
    .createHmac("sha256", key)
    .update(`${id}.${timestamp}.${rawBody}`)
    .digest("base64");

  return headers["webhook-signature"].split(" ").some((signature) => {
    const [version, value] = signature.split(",");
    return (
      version === "v1" &&
      value.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(value), Buffer.from(expected))
    );
  });
}

Повторы

Неудачная доставка повторяется: всего 8 попыток, с паузами сразу, 5 с, 5 мин, 30 мин, 2 ч, 5 ч, 10 ч, 10 ч — чуть больше суток. Если все попытки неудачны, доставка отмечается «не доставлено»; событие остаётся в GET /events.

  • Одно событие может прийти больше одного раза: например, если ваш ответ не дошёл до нас. Отличайте повтор по webhook-id и не обрабатывайте одно событие дважды.
  • Порядок доставки не гарантирован: повтор старого события может прийти после нового. Время события — в created_at.
  • Если ваш адрес был недоступен долго, прочитайте пропущенное через GET /events.

Сбои адреса

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

  • Если полоса длится 24 часа, при следующей неудаче приходит уведомление.
  • Если 3 суток — адрес выключается, события на него больше не отправляются, и об этом тоже приходит уведомление. Включите адрес в панели, когда он заработает.

В панели у каждого адреса видны его доставки: что отправлено, что ответил адрес и когда следующая попытка. Доставку «не доставлено» можно повторить: она встаёт в очередь и уходит в течение нескольких секунд. Кнопка «Проверить» отправляет событие webhook.test — удобно, чтобы проверить приём до первых настоящих событий; на счёт сбоев адреса проверка не влияет.

Секрет

Секрет меняется в панели кнопкой «Новый секрет» — с подтверждением кодом. Прежний секрет действует ещё 24 часа: всё это время запросы подписываются обоими, и вы успеваете заменить секрет в интеграции без потери событий.

Секрет, как и ключ API, храните на сервере. Если он утёк, смените его.

На этой странице