Integre ao seu app
Callbacks de resultado
Os webhooks reduzem a latência entre o fim de uma tarefa e o momento em que o seu app fica sabendo. São uma otimização de entrega: três tentativas, e depois o gateway para de tentar. O endpoint de status continua sendo a fonte da verdade.
#Registre o callback no momento do envio
Passe webhook_url na requisição de envio. Adicione webhook_secret e toda entrega é assinada — sem ele, nenhuma é.
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"
}'Precisa ser roteável publicamente
localhost, 127.0.0.1, faixas privadas, link-local e endereços de metadados de nuvem são rejeitados quando a tarefa é enviada — não no momento da entrega. Use um túnel durante o desenvolvimento.
http ou https, resolvível no momento do envio
O hostname é resolvido e cada endereço retornado é verificado. Um nome que não resolve faz o envio falhar.
A entrega é de melhor esforço
Três novas tentativas; depois o gateway para e apenas registra em log. O endpoint de status continua sendo a fonte da verdade.
localhost é rejeitado quando a tarefa é enviada
#Cabeçalhos e payload
| Cabeçalho | Enviado quando | O que fazer com ele |
|---|---|---|
| X-Xiaoxiang-Signature | Somente quando a tarefa foi enviada com webhook_secret. | sha256=<HMAC-SHA256 em hex do corpo bruto>. Verifique antes de fazer o parse, com comparação em tempo constante. |
| X-Xiaoxiang-Event | Toda entrega. | task.completed ou task.failed — roteie por este valor em vez de deduzir a intenção pelo payload. |
| X-Xiaoxiang-Event-Id | Toda entrega. | Estável entre os reenvios do mesmo evento. Armazene-o e descarte repetições — esta é sua chave de deduplicação. |
| Campo | Tipo | Presente | Descrição |
|---|---|---|---|
| event_id | string | Sempre | Igual a X-Xiaoxiang-Event-Id. Faça a deduplicação por ele. |
| event | string | Sempre | "task.completed" ou "task.failed". |
| task_id | string | Sempre | O id retornado pela chamada de envio — sua chave de junção. |
| status | string | Sempre | Status final no vocabulário de vídeo — "completed" ou "failed", mesmo para tarefas de imagem e música. |
| model | string | Quando conhecido | Modelo que gerou a tarefa. Omitido quando desconhecido. |
| url | string | Em caso de sucesso | URL do resultado quando a tarefa teve sucesso. Prefira /content para um link estável da plataforma. |
| fail_reason | string | Em caso de falha | Presente apenas em caso de falha. |
| quota | integer | Sempre | Cota efetivamente consumida, em unidades internas. Faça a conciliação com o log de uso, não com este campo. |
| created_at | integer | Sempre | Segundos Unix, envio da tarefa. |
| finished_at | integer | Sempre | Segundos Unix, transição para o estado final. |
{
"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
}#Verifique o corpo bruto da requisição
Calcule o HMAC-SHA256 sobre os bytes exatos que você recebeu, antes de qualquer parse de JSON — serializar o payload de novo muda os bytes e quebra a comparação. Compare em tempo constante e rejeite se não bater.
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();
}#Entrega e deduplicação
Confirme em até 15 segundos
Esse é o timeout da entrega. Qualquer coisa mais lenta conta como tentativa falha, então coloque o trabalho em fila e retorne 2xx primeiro.
Deduplique pelo id do evento
X-Xiaoxiang-Event-Id é estável entre as novas tentativas. Armazene-o e descarte repetições antes de mexer no estado do negócio.
Espere três novas tentativas e depois silêncio
Entregas que falham são repetidas após 5s, 30s e 2min. Depois disso o gateway só registra em log — nada mais vai chegar.
#Mantenha um verificador de conciliação
A entrega do webhook é de melhor esforço
Ainda com problemas?
Envie para nós o ID da requisição ou a mensagem de erro e vamos investigar.