Перейти к содержимому
API Reference

Вебхуки

Получайте POST-уведомление о завершении генерации с проверкой подписи HMAC-SHA256 и автоматическими ретраями.

Передайте webhook_url при постановке генерации — и по её завершении Clipia отправит POST на этот URL с результатом. Это избавляет от опроса статуса. Каждая доставка подписана HMAC-SHA256 (заголовок X-Clipia-Signature), а при сбое повторяется до 6 раз с экспоненциальной задержкой.

Payload

При успехе:

{
  "request_id": "764cabcf-b745-4b3e-ae38-1200304cf45b",
  "status": "OK",
  "payload": {
    "model": "nano-banana-2",
    "output": { "images": [{ "url": "https://media.clipia.ai/works/result.png" }] },
    "cost": 12
  }
}

При ошибке:

{
  "request_id": "764cabcf-b745-4b3e-ae38-1200304cf45b",
  "status": "ERROR",
  "error": { "code": "GENERATION_FAILED", "message": "..." }
}

Поле output принимает одну из трёх форм — в зависимости от типа модели: images: [{ url }], video: { url } либо audio: { url } для синтеза речи.

Payload вебхука компактнее ответа REST

Два отличия, о которые чаще всего спотыкаются интеграции:

  1. original_url (оригинал в полном качестве) в вебхук не кладётся — если он нужен, запросите результат по GET /v1/requests/:id.
  2. При мульти-выдаче изображений в вебхук попадает только первый URL; полный список — там же, по request_id.

Поля payload

Prop

Type

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

Каждая доставка несёт три заголовка:

X-Clipia-Webhook-Id: 1f2e3d4c-5b6a-7890-abcd-ef0123456789
X-Clipia-Timestamp: 1717243200
X-Clipia-Signature: t=1717243200,v1=5257a869e7...

Подпись считается как HMAC_SHA256(secret, "{timestamp}.{raw_body}"), где secret — webhook signing secret из консоли разработчика. Сравнивайте подпись над сырым телом запроса (до парсинга JSON) и используйте constant-time сравнение.

import crypto from "node:crypto";

const secret = process.env.CLIPIA_WEBHOOK_SECRET;

function verify(req) {
  const sig = req.headers["x-clipia-signature"];        // "t=...,v1=..."
  const parts = Object.fromEntries(sig.split(",").map(p => p.split("=")));
  const signed = `${parts.t}.${req.rawBody}`;
  const expected = crypto.createHmac("sha256", secret).update(signed).digest("hex");
  const ok = crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; // окно 5 мин
  return ok && fresh;
}
import hashlib
import hmac
import os
import time

SECRET = os.environ["CLIPIA_WEBHOOK_SECRET"].encode()

def verify(headers: dict, raw_body: bytes) -> bool:
    sig = headers["X-Clipia-Signature"]               # "t=...,v1=..."
    parts = dict(p.split("=", 1) for p in sig.split(","))
    signed = f"{parts['t']}.".encode() + raw_body
    expected = hmac.new(SECRET, signed, hashlib.sha256).hexdigest()
    ok = hmac.compare_digest(parts["v1"], expected)
    fresh = abs(time.time() - int(parts["t"])) < 300   # 5-минутное окно
    return ok and fresh

Проверяйте свежесть

Отвергайте доставку, если X-Clipia-Timestamp отличается от текущего времени более чем на 5 минут — это защищает от повторного воспроизведения старых запросов.

Доставка и ретраи

  • Отвечайте кодом 2xx в течение 10 секунд.
  • При не-2xx или таймауте доставка повторяется с экспоненциальной задержкой, до 6 попыток.
  • История доставок видна в консоли разработчика — удобно для отладки.

Обрабатывайте идемпотентно

Одна и та же доставка может прийти повторно. Дедуплицируйте по request_id (или X-Clipia-Webhook-Id), прежде чем выполнять побочные эффекты.