aisplice

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 è.

submit.sh
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

L'host della callback viene risolto e controllato prima che il task sia accettato, quindi un indirizzo privato o di loopback fa fallire l'invio stesso — non la consegna un'ora dopo. Durante lo sviluppo, punta webhook_url a un tunnel pubblico, oppure omettilo e usa il polling.

#Header e payload

HeaderInviato quandoCome usarlo
X-Xiaoxiang-SignatureSolo 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-EventOgni consegna.task.completed o task.failed — instrada in base a questo invece di ricavare l'intento dal payload.
X-Xiaoxiang-Event-IdOgni consegna.Stabile tra i tentativi dello stesso evento. Salvalo e scarta le ripetizioni — è la tua chiave di deduplicazione.
CampoTipoPresenteDescrizione
event_idstringSempreCorrisponde a X-Xiaoxiang-Event-Id. Usalo per deduplicare.
eventstringSempre"task.completed" o "task.failed".
task_idstringSempreL'id restituito dalla chiamata di invio — la tua chiave di join.
statusstringSempreStato finale nel vocabolario dei video — "completed" o "failed", anche per i task di immagini e musica.
modelstringQuando notoModello che ha prodotto il task. Omesso se sconosciuto.
urlstringIn caso di successoURL del risultato quando il task è riuscito. Preferisci /content per un link stabile della piattaforma.
fail_reasonstringIn caso di errorePresente solo in caso di errore.
quotaintegerSempreQuota effettivamente consumata, in unità interne. Riconcilia con il registro di utilizzo, non con questo campo.
created_atintegerSempreSecondi Unix, invio del task.
finished_atintegerSempreSecondi Unix, passaggio allo stato finale.
delivery.json
{
  "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

Esegui un job a bassa frequenza sui task ancora non conclusi nel tuo database — basta un deploy durante una finestra di consegna per perderne uno. Interroga l'endpoint di stato, ripara la riga e considera il webhook solo come un guadagno di latenza.

Non hai ancora risolto?

Inviaci l'ID richiesta o il messaggio di errore e verificheremo.