Webhooki
Podaj webhook_url w POST /v1/generate, a Jantaris wyśle na ten adres zapytanie POST, gdy zadanie dojdzie do succeeded albo failed. Bez pętli odpytywania.
Treść
Ciało to ten sam JSON, który zwraca 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 | Wartość |
|---|---|
Content-Type | application/json |
x-jantaris-event | task.succeeded albo task.failed |
x-jantaris-signature | sha256=<hex>, HMAC-SHA256 surowego ciała zapytania z twoim sekretem webhooka |
Sekret webhooka znajdziesz w panelu. Trzymaj go na serwerze, tak jak klucz API.
Weryfikacja podpisu
Licz HMAC-SHA256 z surowych bajtów ciała dokładnie w takiej postaci, w jakiej przyszły, przed parsowaniem JSON-a, i porównuj z nagłówkiem w stałym czasie. Zapytania z niezgodnym podpisem odrzucaj.
# pip install flask
import hmac, hashlib, os
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = os.environ["JANTARIS_WEBHOOK_SECRET"].encode()
seen = set() # na produkcji tabela w bazie
@app.post("/jantaris-hook")
def jantaris_hook():
raw = request.get_data() # surowe bajty
expected = "sha256=" + hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
received = request.headers.get("x-jantaris-signature", "")
if not hmac.compare_digest(expected, received):
abort(401)
task = request.get_json()
event = request.headers.get("x-jantaris-event")
if task["id"] in seen:
return "", 200 # duplikat
seen.add(task["id"])
enqueue_processing(task, event) # twój kod: wrzuć do kolejki i szybko odpowiedz
return "", 200// npm install express
import express from "express";
import crypto from "node:crypto";
const app = express();
const SECRET = process.env.JANTARIS_WEBHOOK_SECRET;
const seen = new Set(); // na produkcji tabela w bazie
app.post("/jantaris-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-jantaris-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);
queueMicrotask(() => processTask(task, req.get("x-jantaris-event"))); // twój kod
res.sendStatus(200);
});
app.listen(3000);Częsty błąd: weryfikacja na JSON.stringify(req.body) po parserze JSON. Ponownie zserializowany JSON różni się od oryginalnych bajtów i podpis się nie zgodzi. Licz podpis z surowego ciała.
Ponowienia
Pierwsza próba doręczenia idzie od razu po zakończeniu zadania. Jeśli twój endpoint nie odpowie statusem 2xx (albo nie odpowie wcale), Jantaris ponawia do 5 razy, z coraz dłuższymi przerwami:
| Ponowienie | Odstęp od poprzedniej próby |
|---|---|
| 1 | 10 s |
| 2 | 1 min |
| 3 | 5 min |
| 4 | 30 min |
| 5 | 2 h |
Po ostatnim nieudanym ponowieniu doręczenie przepada. Samo zadanie dalej jest dostępne przez GET /v1/tasks/{id}.
Zasady obsługi
- Odpowiadaj 2xx szybko. Zapisz treść, cięższą pracę (pobieranie plików, transkodowanie) wrzuć do kolejki i zwróć odpowiedź. Wolna odpowiedź liczy się jak błąd i uruchamia ponowienie.
- Odrzucaj duplikaty po id zadania. Ponowienie może dojść, mimo że pierwsze doręczenie już obsłużyłeś, np. gdy twoja odpowiedź 200 zginęła w sieci. Zapisuj obsłużone id i pomijaj powtórki.
- Pobieraj wyniki od razu. Linki w
outputsą tymczasowe. - Miej plan B. Jeśli serwer leżał dłużej niż okno ponowień, wywołaj
GET /v1/tasks/{id}dla zadań, o których nie dostałeś informacji.
Testy lokalne
webhook_url musi być osiągalny z internetu. W trakcie pracy wystaw lokalny port przez dowolny tunel i podaj publiczny adres jako webhook_url. Możesz też odtworzyć zapisaną treść curl-em, samodzielnie ją podpisując:
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 "$JANTARIS_WEBHOOK_SECRET" | sed 's/^.* //')
curl -X POST http://localhost:3000/jantaris-hook \
-H "Content-Type: application/json" \
-H "x-jantaris-event: task.succeeded" \
-H "x-jantaris-signature: sha256=$SIG" \
-d "$BODY"








