Webhook
Alih-alih melakukan polling, berikan webhook_url saat menjalankan model. Saat permintaan mencapai status final (succeeded, failed atau canceled), kami mengirimkan POST bertanda tangan ke URL tersebut.
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": "..."}'
Event
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" } ]
}
}
typeadalahrequest.succeeded,request.failedataurequest.canceled.dataadalah objek permintaan lengkap, persis seperti yang dikembalikanGET /requests/{id}.idtetap sama untuk suatu permintaan dan status finalnya. Gunakan id ini untuk mengabaikan pengiriman yang sudah diproses.
Pengiriman dan percobaan ulang
Berikan respons dengan status 2xx apa pun dalam 10 detik. Hal lainnya (atau timeout) akan dicoba ulang: hingga total 6 kali, setelah 30 detik, 2 menit, 10 menit, 1 jam, dan 6 jam. Lakukan pekerjaan lambat setelah memberikan respons, bukan sebelumnya.
Memverifikasi tanda tangan
Setiap kunci memiliki secret penandatanganannya sendiri. Baca secret tersebut satu kali dari GET https://api.doitong.com/v1/account (webhook_signing_secret, diawali dengan whsec_) dan simpan di dekat kunci.
Header Doitong-Signature berbentuk t=<unix time>,v1=<signature>, dengan tanda tangan berupa HMAC-SHA256 heksadesimal dari "<t>.<raw request body>" menggunakan secret Anda sebagai kunci. Untuk memverifikasi:
- Ambil byte body mentah persis seperti yang diterima (sebelum parsing JSON apa pun).
- Hitung HMAC-SHA256 dari
t + "." + bodydengan secret Anda, sebagai heksadesimal huruf kecil. - Bandingkan dengan
v1dalam waktu konstan. - Tolak event jika
tberjarak lebih dari 5 menit dari waktu Anda (perlindungan terhadap replay).
Receiver lengkap dalam Node.js (Express) dan 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
Tips
- Berikan respons dengan cepat dan antrekan pemrosesan Anda sendiri.
- Secret terkait dengan kunci API yang memulai eksekusi. Kunci pada layanan yang berbeda memiliki secret yang berbeda.