Integralo nella tua app
Callback dei risultati
I webhook riducono la latenza tra la fine di un task e il momento in cui la tua app lo sa. Sono un'ottimizzazione della consegna: tre tentativi, poi il gateway smette di provare. L'endpoint di stato resta la fonte di verità.
#Registra la callback al momento dell'invio
Passa webhook_url nella richiesta di invio. Aggiungi webhook_secret e ogni consegna sarà firmata — senza, nessuna lo è.
curl https://www.aisplice.store/v1/videos \
-H "Authorization: Bearer sk-YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: video-job-8842" \
-d '{
"model": "doubao-seedance-2.5",
"prompt": "A cinematic product scene",
"webhook_url": "https://your.app/hooks/ai-tasks",
"webhook_secret": "whsec_replace_me"
}'Deve essere instradabile pubblicamente
localhost, 127.0.0.1, gli intervalli privati, gli indirizzi link-local e quelli dei metadati cloud vengono rifiutati all'invio del task — non al momento della consegna. Durante lo sviluppo usa un tunnel.
http o https, risolvibile al momento dell'invio
L'hostname viene risolto e ogni indirizzo restituito viene controllato. Un nome che non si risolve fa fallire l'invio.
La consegna è best-effort
Tre tentativi, poi il gateway si ferma e si limita a registrare. L'endpoint di stato resta la fonte di verità.
localhost viene rifiutato all'invio del task
#Header e payload
| Header | Inviato quando | Come usarlo |
|---|---|---|
| X-Xiaoxiang-Signature | Solo se il task è stato inviato con webhook_secret. | sha256=<hex HMAC-SHA256 del corpo grezzo>. Verificalo prima del parsing, con un confronto a tempo costante. |
| X-Xiaoxiang-Event | Ogni consegna. | task.completed o task.failed — instrada in base a questo invece di ricavare l'intento dal payload. |
| X-Xiaoxiang-Event-Id | Ogni consegna. | Stabile tra i tentativi dello stesso evento. Salvalo e scarta le ripetizioni — è la tua chiave di deduplicazione. |
| Campo | Tipo | Presente | Descrizione |
|---|---|---|---|
| event_id | string | Sempre | Corrisponde a X-Xiaoxiang-Event-Id. Usalo per deduplicare. |
| event | string | Sempre | "task.completed" o "task.failed". |
| task_id | string | Sempre | L'id restituito dalla chiamata di invio — la tua chiave di join. |
| status | string | Sempre | Stato finale nel vocabolario dei video — "completed" o "failed", anche per i task di immagini e musica. |
| model | string | Quando noto | Modello che ha prodotto il task. Omesso se sconosciuto. |
| url | string | In caso di successo | URL del risultato quando il task è riuscito. Preferisci /content per un link stabile della piattaforma. |
| fail_reason | string | In caso di errore | Presente solo in caso di errore. |
| quota | integer | Sempre | Quota effettivamente consumata, in unità interne. Riconcilia con il registro di utilizzo, non con questo campo. |
| created_at | integer | Sempre | Secondi Unix, invio del task. |
| finished_at | integer | Sempre | Secondi Unix, passaggio allo stato finale. |
{
"event_id": "task_8f21c...:task.completed",
"event": "task.completed",
"task_id": "8f21c0d2-...",
"status": "completed",
"model": "doubao-seedance-2.5",
"url": "https://cdn.example.com/renders/8f21c0d2.mp4",
"quota": 128000,
"created_at": 1789000000,
"finished_at": 1789000184
}#Verifica il corpo grezzo della richiesta
Calcola HMAC-SHA256 sui byte esatti che hai ricevuto, prima di qualsiasi parsing JSON — riserializzare il payload cambia i byte e rompe il confronto. Confronta a tempo costante e rifiuta in caso di mancata corrispondenza.
import crypto from "node:crypto";
// Express: express.raw({ type: "application/json" }) — not express.json().
export function handleTaskWebhook(req, res) {
const received = req.get("X-Xiaoxiang-Signature") ?? "";
const expected =
"sha256=" +
crypto.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(req.body) // Buffer of the raw body
.digest("hex");
const a = Buffer.from(received);
const b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).end();
}
const event = JSON.parse(req.body.toString("utf8"));
// Deduplicate first, acknowledge fast, then do the work off-request.
if (!seen.add(req.get("X-Xiaoxiang-Event-Id"))) return res.status(200).end();
queue.push(event);
res.status(200).end();
}#Consegna e deduplicazione
Conferma entro 15 secondi
È il timeout di consegna. Qualsiasi cosa più lenta conta come tentativo fallito, quindi metti il lavoro in coda e restituisci prima un 2xx.
Deduplica in base all'id dell'evento
X-Xiaoxiang-Event-Id è stabile tra i tentativi. Salvalo e scarta le ripetizioni prima di toccare lo stato di business.
Aspettati tre tentativi, poi silenzio
Le consegne fallite vengono ritentate dopo 5s, 30s e 2min. Dopodiché il gateway si limita a registrare — non arriverà nient'altro.
#Mantieni un poller di riconciliazione
La consegna dei webhook è best-effort
Non hai ancora risolto?
Inviaci l'ID richiesta o il messaggio di errore e verificheremo.