내 앱에 통합하기

결과 콜백

웹훅은 작업 완료와 앱이 이를 알게 되는 시점 사이의 지연을 줄입니다. 전송 최적화일 뿐이며, 세 번 시도한 뒤 게이트웨이는 더 이상 시도하지 않습니다. 기준은 언제나 상태 엔드포인트입니다.

#제출 시 콜백 등록

제출 요청에 webhook_url을 넣으세요. webhook_secret을 추가하면 모든 전송에 서명이 붙고 — 없으면 아무것도 서명되지 않습니다.

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"
  }'

공개적으로 라우팅 가능한 주소여야 함

localhost, 127.0.0.1, 사설 대역, 링크 로컬, 클라우드 메타데이터 주소는 전송 시점이 아니라 작업 제출 시점에 거부됩니다. 개발 중에는 터널을 사용하세요.

http 또는 https, 제출 시점에 확인(resolve) 가능해야 함

호스트 이름을 확인(resolve)하고 반환된 모든 주소를 검사합니다. 확인되지 않는 이름이면 제출이 실패합니다.

전송은 best-effort 방식

세 번 재시도한 뒤에는 게이트웨이가 전송을 멈추고 로그만 남깁니다. 기준은 언제나 상태 엔드포인트입니다.

localhost는 작업 제출 시 거부됨

콜백 호스트는 작업이 접수되기 전에 확인(resolve)·검사되므로, 사설 또는 루프백 주소이면 제출 자체가 실패합니다 — 한 시간 뒤 전송 단계가 아닙니다. 개발 중에는 webhook_url을 공개 터널로 지정하거나, 아예 빼고 폴링하세요.

#헤더와 페이로드

헤더전송 조건활용 방법
X-Xiaoxiang-Signature작업을 webhook_secret과 함께 제출한 경우에만.sha256=<원본 본문의 HMAC-SHA256 16진수 값>. 파싱하기 전에 상수 시간 비교로 검증하세요.
X-Xiaoxiang-Event모든 전송.task.completed 또는 task.failed — 페이로드에서 의도를 다시 추론하지 말고 이 값으로 분기하세요.
X-Xiaoxiang-Event-Id모든 전송.같은 이벤트가 재시도되어도 변하지 않습니다. 저장해 두고 중복은 버리세요 — 이것이 중복 제거 키입니다.
필드타입포함 여부설명
event_idstring항상X-Xiaoxiang-Event-Id와 같습니다. 이 값으로 중복을 제거하세요.
eventstring항상"task.completed" 또는 "task.failed".
task_idstring항상제출 호출이 반환한 id — 내 시스템과 연결할 키입니다.
statusstring항상동영상 용어 기준의 최종 상태 — 이미지·음악 작업도 "completed" 또는 "failed"를 사용합니다.
modelstring알려진 경우작업을 생성한 모델입니다. 알 수 없으면 생략됩니다.
urlstring성공 시작업 성공 시 결과 URL입니다. 안정적인 플랫폼 링크가 필요하면 /content를 사용하세요.
fail_reasonstring실패 시실패 시에만 포함됩니다.
quotainteger항상실제로 소비된 한도(내부 단위)입니다. 대조는 이 필드가 아닌 사용량 로그를 기준으로 하세요.
created_atinteger항상작업 제출 시각(Unix 초).
finished_atinteger항상최종 상태 전환 시각(Unix 초).
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
}

#원본 요청 본문 검증

JSON을 파싱하기 전에, 받은 바이트 그대로 HMAC-SHA256을 계산하세요 — 페이로드를 다시 직렬화하면 바이트가 바뀌어 비교가 깨집니다. 상수 시간으로 비교하고 일치하지 않으면 거부하세요.

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();
}

#전송과 중복 제거

15초 안에 응답

이것이 전송 타임아웃입니다. 더 늦으면 실패한 시도로 간주되므로, 작업은 큐에 넣고 먼저 2xx를 반환하세요.

이벤트 ID로 중복 제거

X-Xiaoxiang-Event-Id는 재시도 간에 변하지 않습니다. 비즈니스 상태를 변경하기 전에 저장해 두고 중복은 버리세요.

세 번 재시도 후에는 더 이상 오지 않음

실패한 전송은 5초, 30초, 2분 후에 재시도됩니다. 그 뒤로 게이트웨이는 로그만 남깁니다 — 더 이상 아무것도 오지 않습니다.

#대조용 폴러 유지

웹훅 전송은 best-effort 방식

데이터베이스에서 아직 최종 상태가 아닌 작업을 대상으로 저빈도 배치를 실행하세요 — 전송 시점에 배포가 겹치기만 해도 하나를 놓칠 수 있습니다. 상태 엔드포인트를 조회해 행을 바로잡고, 웹훅은 순전히 지연 시간을 줄이는 수단으로 다루세요.

아직 해결되지 않았나요?

요청 ID나 오류 메시지를 보내 주시면 확인해 드리겠습니다.