Webhooki
Zamiast co chwilę pytać o stan zadania, możesz dać Kadrarisowi adres, pod który sam wyśle wynik. Wpisz webhook_url w POST /v1/generate, a gdy zadanie osiągnie succeeded lub failed, na ten adres przyjdzie zapytanie POST.
Co przychodzi w ciele
Dokładnie ten sam JSON, który zwróciłby GET /v1/tasks/{id}:
{
"id": "task_8f2k...",
"status": "succeeded",
"model": "seedance-2.5",
"output": [{ "type": "video", "url": "https://...",
"duration": 5, "width": 1280, "height": 720 }],
"cost": { "amount": "3.10", "currency": "PLN" },
"error": null,
"created_at": "2026-09-27T10:15:02Z",
"completed_at": "2026-09-27T10:16:41Z"
}Nagłówki
| Nagłówek | Zawartość |
|---|---|
Content-Type | application/json |
x-kadraris-event | task.succeeded lub task.failed |
x-kadraris-signature | sha256=<hex> - HMAC-SHA256 z surowego ciała, liczony twoim sekretem webhooka |
Sekret webhooka jest w panelu. Traktuj go jak klucz API: tylko na serwerze.
Sprawdzenie podpisu
Kolejność ma znaczenie:
- Weź surowe bajty ciała, zanim cokolwiek je sparsuje.
- Policz z nich HMAC-SHA256 swoim sekretem.
- Porównaj wynik z nagłówkiem funkcją działającą w stałym czasie.
- Przy niezgodności odpowiedz
401i nic dalej nie rób.
# pip install flask
import hmac, hashlib, os
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["KADRARIS_WEBHOOK_SECRET"].encode()
seen = set() # w produkcji: tabela w bazie
@app.post("/kadraris-hook")
def kadraris_hook():
raw = request.get_data() # bajty przed parsowaniem
digest = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
expected = "sha256=" + digest
received = request.headers.get("x-kadraris-signature", "")
if not hmac.compare_digest(expected, received):
abort(401)
task = request.get_json()
event = request.headers.get("x-kadraris-event")
if task["id"] in seen:
return "", 200 # to już było
seen.add(task["id"])
# twoja funkcja: odłóż pracę do kolejki
enqueue_processing(task, event)
return "", 200// npm install express
import express from "express";
import crypto from "node:crypto";
const app = express();
const SECRET = process.env.KADRARIS_WEBHOOK_SECRET;
const seen = new Set(); // w produkcji: tabela w bazie
app.post(
"/kadraris-hook",
express.raw({ type: "application/json" }),
(req, res) => {
const expected = "sha256=" + crypto
.createHmac("sha256", SECRET)
.update(req.body)
.digest("hex");
const received = req.get("x-kadraris-signature") ?? "";
const ok =
expected.length === received.length &&
crypto.timingSafeEqual(
Buffer.from(expected), Buffer.from(received));
if (!ok) return res.sendStatus(401);
const task = JSON.parse(req.body.toString("utf8"));
if (seen.has(task.id)) return res.sendStatus(200);
seen.add(task.id);
// twoja funkcja, uruchomiona po odpowiedzi
queueMicrotask(() =>
processTask(task, req.get("x-kadraris-event")));
res.sendStatus(200);
},
);
app.listen(3000);Najczęstsza pułapka to liczenie podpisu z JSON.stringify(req.body) po tym, jak ciało przeszło przez parser. Taki ponownie zserializowany tekst nie jest identyczny z tym, co wysłaliśmy, więc podpis nigdy się nie zgodzi. Podpis zawsze liczysz z oryginalnych bajtów.
Gdy twój serwer nie odbierze
Pierwsza próba wychodzi zaraz po zakończeniu zadania. Jeżeli endpoint nie odpowie kodem 2xx albo milczy, ponawiamy do 5 razy, z coraz dłuższą przerwą:
| Próba ponowna | Przerwa od poprzedniej |
|---|---|
| 1 | 10 s |
| 2 | 1 min |
| 3 | 5 min |
| 4 | 30 min |
| 5 | 2 h |
Jeśli i piąta próba się nie uda, powiadomienie przepada. Zadanie nadal odczytasz przez GET /v1/tasks/{id}.
Dobre praktyki po stronie odbiorcy
- Odpowiadaj od razu. Zapisz treść, a pobieranie plików czy konwersję wrzuć do kolejki. Za długa odpowiedź liczy się jak nieudana i wywołuje ponowienie.
- Pomijaj powtórki po id zadania. To samo powiadomienie może przyjść dwa razy, np. gdy twoje 200 nie dotarło do nas przez sieć. Zapamiętuj obsłużone id.
- Ściągaj pliki bez zwłoki. Adresy w
outputwygasają. - Miej zapasową ścieżkę. Po dłuższej awarii, dłuższej niż okno ponowień, odpytaj
GET /v1/tasks/{id}o zadania, które nie dały znaku życia.
Test na własnym komputerze
webhook_url musi być widoczny z internetu. Na czas pracy wystaw lokalny port dowolnym tunelem i jego publiczny adres podaj jako webhook_url. Możesz też zasymulować powiadomienie curl-em, podpisując treść samodzielnie:
BODY='{"id":"task_test","status":"succeeded","model":"gpt-image-2.5","output":[],"cost":{"amount":"0.00","currency":"PLN"},"error":null}'
SIG=$(printf '%s' "$BODY" \
| openssl dgst -sha256 -hmac "$KADRARIS_WEBHOOK_SECRET" \
| sed 's/^.* //')
curl -X POST http://localhost:3000/kadraris-hook \
-H "Content-Type: application/json" \
-H "x-kadraris-event: task.succeeded" \
-H "x-kadraris-signature: sha256=$SIG" \
-d "$BODY"






















