Webhooks
Au lieu d'effectuer des interrogations périodiques, fournissez une webhook_url à une exécution. Lorsque la requête atteint un état final (succeeded, failed ou
canceled), nous envoyons un POST signé à cette URL.
curl -X POST "https://api.doitong.ru/v1/run/{model_id}?webhook_url=https://example.com/hooks/doitong" \
-H "Authorization: Bearer $DOITONG_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "..."}'
L'événement
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" } ]
}
}
typevautrequest.succeeded,request.failedourequest.canceled.dataest l'objet de requête complet, exactement tel que renvoyé parGET /requests/{id}.idest stable pour une requête et son état final. Utilisez-le pour ignorer une livraison déjà traitée.
Livraisons et nouvelles tentatives
Répondez avec n'importe quel statut 2xx dans les 10 secondes. Dans le cas contraire (ou en cas d'expiration du délai), une nouvelle tentative est effectuée : jusqu'à 6 tentatives au total,
après 30 secondes, 2 minutes, 10 minutes, 1 heure et 6 heures. Effectuez les traitements longs après avoir répondu, et non avant.
Vérifier la signature
Chaque clé possède son propre secret de signature. Lisez-le une fois avec GET https://api.doitong.ru/v1/account (webhook_signing_secret,
il commence par whsec_) et stockez-le avec la clé.
L'en-tête Doitong-Signature est t=<unix time>,v1=<signature>, où la signature est le
HMAC-SHA256 hexadécimal de "<t>.<raw request body>", calculé avec votre secret comme clé. Pour vérifier :
- Récupérez les octets bruts du corps exactement tels qu'ils ont été reçus (avant toute analyse JSON).
- Calculez le HMAC-SHA256 de
t + "." + bodyavec votre secret, en hexadécimal minuscule. - Comparez-le avec
v1en temps constant. - Rejetez l'événement lorsque
tdiffère de votre horloge de plus de 5 minutes (protection contre les rejeux).
Un récepteur complet en Node.js (Express) et 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
Conseils
- Répondez rapidement et mettez vos propres traitements en fichier d'attente.
- Le secret appartient à la clé API qui a démarré l'exécution. Les clés de services différents ont des secrets différents.