APIGrid

Вебхуки

Вместо опроса статуса результат может прийти на ваш сервер сам — сразу после того, как задача завершится.

Как подключить

Передайте адрес своего обработчика в поле webhook_url при создании задачи. Адрес должен быть публичным и начинаться с https://.

curl -X POST https://api.apigrid.cloud/v1/tasks \
  -H "Authorization: Bearer $APIGRID_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance-2.0-mini",
    "input": {
      "prompt": "Бумажный кораблик плывёт по озеру",
      "duration": 5
    },
    "webhook_url": "https://example.com/apigrid/webhook"
  }'

Когда задача завершится — успешно или с ошибкой, — на этот адрес придёт запрос POST.

Что приходит

В теле — тип события и объект задачи, такой же, как в ответе GET /v1/tasks/{id}:

Тело запроса
{
  "event": "task.completed",
  "data": {
    "id": "7e70388cf461f88d7149b1077e4ad098",
    "object": "task",
    "model": "seedance-2.0-mini",
    "status": "completed",
    "output": {
      "urls": [
        "https://…/video.mp4"
      ],
      "files": [
        {
          "url": "https://…/video.mp4",
          "kind": "video"
        }
      ]
    },
    "error": null,
    "price": {
      "amount": "17.250000",
      "currency": "RUB",
      "refunded": false
    },
    "created_at": "2026-10-15T09:12:04.512Z",
    "started_at": "2026-10-15T09:12:04.880Z",
    "completed_at": "2026-10-15T09:13:40.118Z"
  }
}
event
task.completed — задача готова; task.failed — генерация не удалась, деньги возвращены.
webhook-id
Заголовок: ID события — совпадает с ID задачи. Помогает не обработать событие дважды.
webhook-timestamp
Заголовок: время отправки, Unix-время в секундах.
webhook-signature
Заголовок: подпись вида v1,<hex> — см. ниже.

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

Подпись доказывает, что запрос пришёл от нас и не изменён по дороге. Это HMAC-SHA256 от строки {webhook-id}.{webhook-timestamp}.{тело} в шестнадцатеричном виде с префиксом v1, — формат Standard Webhooks.

Ключ подписи — секрет вебхуков из кабинета: Настройки → Секрет вебхуков. Положите его в переменную APIGRID_WEBHOOK_SECRET:

import hashlib
import hmac
import os
import time

from flask import Flask, abort, request

app = Flask(__name__)
SECRET = os.environ["APIGRID_WEBHOOK_SECRET"].encode()


@app.post("/apigrid/webhook")
def webhook():
    body = request.get_data(as_text=True)  # сырое тело — не пересобирайте JSON
    msg_id = request.headers["webhook-id"]
    ts = request.headers["webhook-timestamp"]
    signature = request.headers["webhook-signature"]

    expected = "v1," + hmac.new(SECRET, f"{msg_id}.{ts}.{body}".encode(), hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, signature):
        abort(401)
    if abs(time.time() - int(ts)) > 300:  # защита от повтора старого запроса
        abort(401)

    event = request.get_json()
    task = event["data"]
    if event["event"] == "task.completed":
        print("Готово:", task["id"], [f["url"] for f in task["output"]["files"]])
    else:
        print("Ошибка:", task["id"], task["error"]["message"])
    return "", 200

Повторные попытки

Ответьте любым кодом 2xx в течение 10 секунд. Если ответа нет или код другой, мы повторим доставку: всего три попытки — сразу, через 10 секунд и через 2 минуты.

  • Отвечайте быстро. Долгую обработку (скачивание файлов, запись в базу) запускайте в фоне после ответа.
  • Будьте готовы к повтору. Одно событие может прийти дважды — проверяйте webhook-id.
  • Вебхук — не единственный источник. Если все попытки не удались, результат по-прежнему доступен через GET /v1/tasks/{id}.