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

Вебхуки

Вебхуки Slicer API — события о загрузках, монтаже, экспорте и публикациях, подпись HMAC-SHA256 и её проверка на Node.js и Python, повторы доставки.

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

Вебхук — адрес вашего сервера, на который Slicer сам отправляет событие, когда что-то произошло: загрузка готова, монтаж закончился, ролик опубликован. Это быстрее и экономнее опроса: не нужно спрашивать статус каждые 5 секунд, и запросы не расходуют лимит.

Добавить вебхук

Нужна область webhooks. Адрес — только публичный https; до 5 вебхуков на аккаунт.

Терминал
curl https://api.slicer.media/v1/webhooks \
  -H "Authorization: Bearer $SLICER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/slicer/webhook","events":["project.succeeded","project.failed","export.succeeded"],"description":"Уведомления в рабочий чат"}'
Ответ
{
  "ok": true,
  "data": {
    "webhook": {
      "id": "5f8e2a10-6c3b-4d7e-9f21-0a1b2c3d4e5f",
      "url": "https://example.com/slicer/webhook",
      "events": ["project.succeeded", "project.failed", "export.succeeded"],
      "description": "Уведомления в рабочий чат",
      "enabled": true,
      "disabledReason": null,
      "failures": 0,
      "lastDeliveryAt": null,
      "createdAt": "2026-10-16T09:30:00.000Z"
    },
    "secret": "whsec_…"
  }
}

secret — ключ подписи. Он показывается один раз: сохраните его в переменной окружения вашего сервера, например SLICER_WEBHOOK_SECRET. Вебхуки можно добавлять и в приложении: Профиль → «Для разработчиков» → «Вебхуки».

События

Событие Когда приходит data
upload.readyзагрузка проверена и готова к монтажу (в том числе импорт по адресу)загрузка
upload.failedфайл не принятзагрузка с причиной в error
project.succeededмонтаж, пересборка или повтор завершилисьпроект
project.failedзапуск не получился, резерв токенов вернулсяпроект с error
project.cancelledзапуск остановленпроект
project.version_readyверсии ролика ждут выбора — приходит после project.succeeded, только если монтаж отдал несколько версий. Сейчас монтаж отдаёт одну, и этого события нет: ждите project.succeededпроект с versionChoice
export.succeededэкспорт готов, файл обновлёнпроект
export.failedэкспорт не получился, прежний ролик сохранёнпроект с error
publication.publishedролик опубликован в соцсетипубликация с url и projectId
publication.failedпубликация не удаласьпубликация с errorCode и projectId
pingпроверка вебхука (POST /v1/webhooks/{id}/test) — отправляется сразу, без повторовwebhookId и message

Список событий может пополняться (Версии и совместимость): пропускайте незнакомые типы, отвечая 2xx.

Формат запроса

Slicer отправляет POST с JSON-телом и служебными заголовками:

Запрос к вашему серверу
POST /slicer/webhook HTTP/1.1
Content-Type: application/json
User-Agent: Slicer-Webhooks/1.0 (+https://docs.slicer.media/api/webhooks/)
Slicer-Signature: t=1792152060,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Slicer-Event: project.succeeded
Slicer-Delivery: 48213

{
  "id": "evt_9a3c1f0e7b2d4c58a1e6f03b7d9c2e41",
  "type": "project.succeeded",
  "createdAt": "2026-10-16T09:41:00.000Z",
  "data": { "id": 318, "title": "Монтаж за 3 минуты", "status": "succeeded", "appUrl": "https://app.slicer.media/projects/318" }
}
  • id — уникальный id события (evt_…); по нему отбрасывайте повторы.
  • type — тип события, он же — в заголовке Slicer-Event.
  • data — объект в том же виде, что отдаёт API (GET /v1/projects/{id}, GET /v1/uploads/{id} или публикация).
  • Slicer-Delivery — номер доставки: по нему доставку легко найти в журнале GET /v1/webhooks/{id}/deliveries.

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

Подпись доказывает, что запрос пришёл от Slicer и не изменён по дороге. Заголовок Slicer-Signature содержит время отправки t (Unix-секунды) и подпись v1 — HMAC-SHA256 в hex от строки <t>.<тело запроса> на ключе secret.

Как проверять:

  1. Возьмите сырое тело запроса — байты как пришли, до разбора JSON. После разбора и повторной сериализации подпись не сойдётся.
  2. Посчитайте HMAC-SHA256 от t, точки и тела на своём секрете.
  3. Сравните с v1 за постоянное время (timingSafeEqual, hmac.compare_digest), а не обычным ==.
  4. Отклоните запрос, если t отличается от текущего времени больше чем на 5 минут, — это защищает от повторной отправки перехваченного запроса.
JavaScript
Python
webhook.mjs
// Node.js 20+: npm install express
import { createHmac, timingSafeEqual } from 'node:crypto';
import express from 'express';

const TOLERANCE_SEC = 5 * 60;

/** true, если подпись верна и запрос не старше 5 минут. */
export function verifySlicerSignature(rawBody, header, secret, nowMs = Date.now()) {
  if (typeof header !== 'string') return false;
  const pairs = header.split(',').map((part) => part.trim().split('='));
  const timestamp = pairs.find(([key]) => key === 't')?.[1];
  const signatures = pairs.filter(([key]) => key === 'v1').map(([, value]) => value);
  if (!timestamp || !/^\d+$/.test(timestamp) || signatures.length === 0) return false;
  if (Math.abs(nowMs / 1000 - Number(timestamp)) > TOLERANCE_SEC) return false;

  const expected = createHmac('sha256', secret).update(`${timestamp}.`).update(rawBody).digest();
  return signatures.some((value) => {
    const received = Buffer.from(value, 'hex');
    return received.length === expected.length && timingSafeEqual(received, expected);
  });
}

const app = express();
const seen = new Set(); // в продакшене — база или Redis

// express.raw: тело приходит Buffer-ом, ровно как его подписал Slicer
app.post('/slicer/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySlicerSignature(req.body, req.get('Slicer-Signature'), process.env.SLICER_WEBHOOK_SECRET)) {
    return res.status(400).send('invalid signature');
  }
  const event = JSON.parse(req.body.toString('utf8'));
  res.sendStatus(204); // ответить быстро, обработать после

  if (seen.has(event.id)) return;
  seen.add(event.id);
  setImmediate(() => {
    if (event.type === 'project.succeeded') console.log(`Ролик готов: ${event.data.title} — ${event.data.appUrl}`);
  });
});

app.listen(3000);

Доставка и повторы

  • Успех — ответ 2xx не дольше чем за 10 секунд. Отвечайте сразу, а долгую работу делайте после ответа или в очереди.
  • Повторы — при другом ответе, таймауте или ошибке соединения: через 1 минуту, 5 минут, 30 минут, 2 часа, 6 часов и 12 часов — всего до 7 попыток.
  • Перенаправления не выполняются. Ответ 3xx считается неудачной доставкой — указывайте окончательный адрес.
  • Порядок не гарантирован, а одно событие может прийти дважды (например, если ваш ответ потерялся). Отбрасывайте повторы по id, а актуальное состояние при сомнениях берите из API.
  • Отключение — после 50 неудачных попыток доставки подряд вебхук выключается (enabled: false, причина в disabledReason), на почту аккаунта приходит письмо. Исправьте сервер и включите вебхук: PATCH /v1/webhooks/{id} с {"enabled": true}.
  • Адрес проверяется при создании и перед каждой отправкой: если он перестал указывать на публичный адрес, доставки не будет.

Проверка и журнал

Терминал
# отправить тестовое событие ping
curl -X POST https://api.slicer.media/v1/webhooks/$WEBHOOK_ID/test -H "Authorization: Bearer $SLICER_TOKEN"

# последние 100 доставок: статус, код ответа, попытки
curl https://api.slicer.media/v1/webhooks/$WEBHOOK_ID/deliveries -H "Authorization: Bearer $SLICER_TOKEN"

Изменить адрес, события или описание — PATCH /v1/webhooks/{id}, удалить — DELETE. Все эндпоинты — в справочнике.

Что дальше