rendergrid
Первые шагиОбзорPlaygroundЗапросыАналитикаКлючиВебхукиБаланс и пополнение
Личный аккаунт
  • Модели и мои цены
  • Партнёрам
  • Уведомления
  • Профиль и безопасность
  • Поддержка
  • Документация
  • Выйти

Вебхуки

БалансПополнить
ПорталПервые шагиОбзорPlaygroundЗапросыАналитикаКлючиВебхукиБаланс и пополнениеАккаунт · Модели и мои ценыПартнёрамУведомленияПрофиль и безопасностьПоддержкаДокументацияВыйти

Загрузка

ОбзорPlaygroundЗапросыКлючи

Узнавайте о готовой картинке сразу

Не опрашивайте статус в цикле: передайте webhook_url в запросе — и мы отправим подписанное уведомление на ваш сервер, как только картинка готова или не получилась.

Документация

Секрет подписи

Каждое уведомление подписано этим секретом: HMAC-SHA256 от времени и тела. Один секрет на аккаунт, для всех ключей и всех уведомлений. Он не то же самое, что API-ключ.

whsec_••••••••••••••••••••••••••••••••
Полный секрет всегда доступен здесь. После замены прежний принимается ещё 24 часа.

Тестовая доставка

Отправим на ваш адрес подписанное тестовое событие test: true — один раз, без повторов. Не чаще одного раза в 30 секунд.

Только публичный HTTPS-адрес: приватные и локальные адреса запрещены.

Результат появится здесь и первой строкой в журнале ниже.

Журнал доставок

последние 50 · счётчики — по ним · время UTC

Как проверить подпись

  1. Возьмите сырое тело запроса — байты как пришли, до разбора JSON.
  2. Склейте строку: значение заголовка X-Timestamp, точка, тело.
  3. Посчитайте HMAC-SHA256 с полным секретом — вместе с whsec_ — и добавьте префикс sha256=.
  4. Сравните с X-Webhook-Signature-256 за постоянное время. Старше 300 секунд по X-Timestamp — отклоняйте.
import hashlib, hmac, re, time

TOLERANCE_SECONDS = 300

def verify_webhook(secret: str, timestamp: str, raw_body: bytes, signature: str) -> bool:
    if not secret or not isinstance(raw_body, bytes):
        return False
    if not re.fullmatch(r"[0-9]{1,12}", timestamp or ""):
        return False
    if not re.fullmatch(r"sha256=[0-9a-f]{64}", signature or ""):
        return False
    if abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS:
        return False
    expected = "sha256=" + hmac.new(
        secret.encode("utf-8"),
        timestamp.encode("ascii") + b"." + raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
import { createHmac, timingSafeEqual } from "node:crypto";

const TOLERANCE_SECONDS = 300;

function verifyWebhook(secret, timestamp, rawBody, signature) {
  if (!secret || !Buffer.isBuffer(rawBody)) return false;
  if (!/^[0-9]{1,12}$/.test(timestamp)) return false;
  if (!/^sha256=[0-9a-f]{64}$/.test(signature)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > TOLERANCE_SECONDS) return false;
  const expected = "sha256=" + createHmac("sha256", Buffer.from(secret, "utf8"))
    .update(timestamp + ".", "ascii")
    .update(rawBody)
    .digest("hex");
  return timingSafeEqual(Buffer.from(expected, "ascii"), Buffer.from(signature, "ascii"));
}

Разбирайте JSON только после успешной проверки. Во время замены секрета подпись может сойтись с текущим или с ещё принимаемым прежним секретом.

Что приходит в заголовках

X-Webhook-Event
creation.completed или creation.failed
X-Webhook-Delivery-ID
Номер доставки: при повторах не меняется
X-Timestamp
Время подписи, секунды Unix; для каждой попытки новое
X-Webhook-Signature-256
sha256= и подпись, 64 символа

Как мы повторяем

  • Любой ответ 2xx — доставлено. Всё остальное — повтор.
  • До пяти попыток: через 60, 120, 240 и 480 секунд после предыдущей.
  • Ждём ответа 10 секунд — подтверждайте приём быстро.
  • Сбой доставки не меняет статус запроса: результат всегда можно забрать по id.