Вебхуки
Вместо опроса статуса результат может прийти на ваш сервер сам — сразу после того, как задача завершится.
Как подключить
Передайте адрес своего обработчика в поле 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"
}
}eventtask.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}.