Webhook
Gib einem Lauf statt regelmäßiger Abfragen eine webhook_url. Wenn die Anfrage einen finalen Status (succeeded, failed oder
canceled) erreicht, senden wir einen signierten POST an diese URL.
curl -X POST "https://api.doitong.com/v1/run/{model_id}?webhook_url=https://example.com/hooks/doitong" \
-H "Authorization: Bearer $DOITONG_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "..."}'
Das Ereignis
POST /hooks/doitong HTTP/1.1
Content-Type: application/json
User-Agent: Doitong-Webhooks/1
Doitong-Request-Id: req_8k2m4q7x1z9c3v5b6n0p2r4t
Doitong-Signature: t=1760961609,v1=5f1c0d6e2b…
{
"id": "evt_8k2m4q7x1z9c3v5b6n0p2r4t_succeeded",
"object": "event",
"type": "request.succeeded",
"created_at": "2026-10-20T12:00:09Z",
"data": {
"id": "req_8k2m4q7x1z9c3v5b6n0p2r4t",
"object": "request",
"status": "succeeded",
"outputs": [ { "type": "video", "url": "https://content.doitong.com/dev-api/…/0-output.mp4" } ]
}
}
typeistrequest.succeeded,request.failedoderrequest.canceled.dataist das vollständige Anfrageobjekt, genau wie es vonGET /requests/{id}zurückgegeben wird.idbleibt für eine Anfrage und ihren finalen Status stabil. Verwende es, um eine bereits verarbeitete Zustellung zu ignorieren.
Zustellungen und Wiederholungen
Antworte innerhalb von 10 Sekunden mit einem beliebigen 2xx-Status. Alles andere (oder eine Zeitüberschreitung) wird wiederholt: insgesamt bis zu 6 Versuche,
nach 30 Sekunden, 2 Minuten, 10 Minuten, 1 Stunde und 6 Stunden. Führe langsame Arbeiten erst nach deiner Antwort aus, nicht vorher.
Signatur verifizieren
Jeder Schlüssel hat sein eigenes Signaturgeheimnis. Lies es einmal über GET https://api.doitong.com/v1/account (webhook_signing_secret
(es beginnt mit whsec_) aus und speichere es zusammen mit dem Schlüssel.
Der Header Doitong-Signature hat das Format t=<unix time>,v1=<signature>, wobei die Signatur der hexadezimale
HMAC-SHA256-Wert von "<t>.<raw request body>" ist, berechnet mit deinem Geheimnis als Schlüssel. So verifizierst du ihn:
- Übernimm die unveränderten Body-Bytes genau so, wie sie empfangen wurden (vor dem Parsen des JSON).
- Berechne den HMAC-SHA256-Wert von
t + "." + bodymit deinem Geheimnis als kleingeschriebenen Hexadezimalwert. - Vergleiche ihn in konstanter Zeit mit
v1. - Lehne das Ereignis ab, wenn
tmehr als 5 Minuten von deiner Uhrzeit abweicht (Schutz vor Replay-Angriffen).
Ein vollständiger Empfänger in Node.js (Express) und Python (Flask):
import crypto from "node:crypto";
import express from "express";
const SECRET = process.env.DOITONG_WEBHOOK_SECRET; // whsec_...
const app = express();
app.post("/hooks/doitong", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("Doitong-Signature") || "";
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
const t = Number(parts.t);
const expected = crypto.createHmac("sha256", SECRET).update(t + "." + req.body).digest("hex");
const given = Buffer.from(parts.v1 || "", "utf8");
const valid =
given.length === expected.length &&
crypto.timingSafeEqual(given, Buffer.from(expected, "utf8")) &&
Math.abs(Date.now() / 1000 - t) < 300;
if (!valid) return res.status(400).send("bad signature");
const event = JSON.parse(req.body);
if (event.type === "request.succeeded") {
console.log(event.data.id, event.data.outputs.map((o) => o.url));
}
res.sendStatus(204);
});
import hashlib
import hmac
import json
import os
import time
from flask import Flask, abort, request
SECRET = os.environ["DOITONG_WEBHOOK_SECRET"].encode() # whsec_...
app = Flask(__name__)
@app.post("/hooks/doitong")
def doitong_hook():
body = request.get_data() # raw bytes
parts = dict(p.split("=", 1) for p in request.headers.get("Doitong-Signature", "").split(",") if "=" in p)
t = parts.get("t", "0")
expected = hmac.new(SECRET, t.encode() + b"." + body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, parts.get("v1", "")) or abs(time.time() - int(t)) > 300:
abort(400)
event = json.loads(body)
if event["type"] == "request.succeeded":
print(event["data"]["id"], [o["url"] for o in event["data"]["outputs"]])
return "", 204
Tipps
- Antworte schnell und stelle deine eigene Verarbeitung in eine Warteschlange.
- Das Geheimnis gehört zum API-Schlüssel, der den Lauf gestartet hat. Schlüssel in verschiedenen Diensten haben unterschiedliche Geheimnisse.