Sume 영상 실행용 서명된 웹훅: 이벤트, 재시도, 검증
Format·Action·Agent Completion 실행이 완료되거나 실패하면 Sume가 HMAC-SHA256 서명 POST를 한 번 보냅니다. 원본 본문을 검증하고 request_id로 중복을 제거하세요.

Sume 실행 웹훅은 Action, Format, Agent Completion 실행이 완료되거나 실패했을 때 Sume가 communication.webhook_url로 보내는 서명된 POST 한 번이며, 폴링 엔드포인트가 반환하는 것과 같은 영수증을 담습니다. <timestamp>.<raw_body>에 대한 HMAC-SHA256으로 서명되므로, 수신기는 원본 본문을 검증하고 request_id로 중복을 제거한 뒤 2xx를 빠르게 반환합니다.
아래 규칙은 Sume의 Run 웹훅 (영문), 생성 Job용 웹훅 (영문), SDK 웹훅 검증 페이지를 바탕으로 합니다. 웹훅 전달은 프로덕션인 api.sume.com에서 동작하고 있으며, 폴링도 백업으로 계속 지원됩니다.
웹훅은 어떻게 요청하나요?
실행을 시작할 때 communication.webhook_url을 보내세요. 세 가지 실행 표면 모두 같은 방식으로 동작합니다.
- URL은 공개 HTTPS여야 하며 최대 2048자입니다. localhost, 사설 네트워크, HTTPS가 아닌 URL은
400 invalid_request로 거부됩니다. communication.callback_url도 별칭으로 받습니다.communication.mode는async(기본값) 또는webhook이지만 설명용일 뿐이며, 실제로 전달을 켜는 것은 URL입니다.- Sume는 전달 시점에 URL을 다시 검증합니다. 리다이렉트는 따라가지 않으므로
3xx는 전달로 치지 않습니다. /v1/avatar-1.0/generate같은 모델 엔드포인트의 생성 Job은mode: "webhook"과webhook_url을 받고, 대신job.*이벤트를 보냅니다.
엔드포인트는 어떤 이벤트를 받나요?
종료 이벤트만 받습니다. 실행은 에이전트 턴 한 번이므로, 그 턴이 클립이나 이미지를 몇 개 만들든 이벤트는 정확히 한 번 발생합니다. 실행을 이어 가면 자체 웹훅을 가진 새 실행이 시작됩니다. 결과는 이벤트 이름이 아니라 status와 payload.status에 담깁니다. 영수증 자체는 Format 실행 수명주기에서 다룹니다.
| 호출한 대상 | 이벤트 | 중복 제거 기준 |
|---|---|---|
| Format 실행 | format.run.terminal | run_id와 같은 request_id |
| Action 실행 | action.run.terminal | run_id와 같은 request_id |
| Agent Completion 실행 | agent.run.terminal | run_id와 같은 request_id |
| 모델 엔드포인트 Job | job.completed, job.failed, job.canceled | job_id |
실행 웹훅 페이로드에는 무엇이 들어 있나요?
봉투가 실행 영수증을 감싸고 있습니다.
status는 실행이 완료됐으면OK, 실패했으면ERROR입니다.outcome은ok,degraded,error중 하나이며, 쓸 수 있는 결과물을 받았는지가 궁금하다면 이 값으로 분기하세요.degraded는 실행이 완료되어artifacts[]에 실제 미디어를 만들었지만 이를output_schema에 투영하지 못해output이null이라는 뜻입니다.request_id는 재시도해도 바뀌지 않으므로 중복 제거 키로 씁니다. 전달 순서는created_at으로 정하세요.payload는GET /v1/{family}-runs/{run_id}의data객체와 바이트 단위로 같으므로, 핸들러 하나로 웹훅과 폴링을 모두 처리할 수 있습니다.- 1 MiB를 넘는 영수증은
payload: null과 오류 코드payload_too_large로 도착합니다.result_url에서 가져오세요. - 취소되거나 건너뛴 실행은 실행 웹훅을 보내지 않습니다. 대신 취소 응답이나 생성 응답을 신뢰하세요.
웹훅 서명은 어떻게 검증하나요?
전달마다 x-sume-webhook-timestamp와 x-sume-webhook-signature: sume-v1=<hex_signature>가 실립니다. TypeScript에서는 아래 핸들러처럼 @sume-com/sdk의 verifyWebhook이 검증을 처리합니다. 중요한 점은 다음과 같습니다.
- 원본 본문을 넘기세요. 키 순서와 공백도 서명 대상의 일부이므로, 파싱했다가 다시 직렬화한 객체는 검증되지 않습니다.
verifyWebhook은async이고, 예외를 던지는 대신false를 반환하며, 상수 시간으로 비교하고, 기본값이300인 재전송 허용 시간toleranceSeconds를 적용합니다.- 서명 시크릿은 대시보드의 웹훅 탭에서 보거나,
account:read가 있는 키로GET /v1/webhooks/signing-secret을 호출해 읽으세요. 이 시크릿은 워크스페이스별로 파생됩니다.SUME_COM_WEBHOOK_SIGNING_SECRET으로 저장하세요. - 서명이 검증되지 않으면
x-sume-webhook-secret-fingerprint헤더를 대시보드에 표시된 지문과 비교하세요. - 실행 웹훅과 Job 웹훅은 시크릿 하나와 서명 방식 하나를 공유하므로, 검증기 하나로 둘 다 처리할 수 있습니다.
event로 라우팅하고, 모르는 이벤트에는204로 응답하세요.
import { verifyWebhook } from "@sume-com/sdk";
export async function POST(request: Request) {
const body = await request.text(); // raw, before any JSON.parse
const ok = await verifyWebhook({
body,
headers: request.headers,
secret: process.env.SUME_COM_WEBHOOK_SIGNING_SECRET!,
});
if (!ok) return new Response("bad signature", { status: 401 });
const event = JSON.parse(body);
await recordTerminalRun(event.request_id, event); // dedupe on request_id
return new Response(null, { status: 204 }); // fast 2xx, then work
}엔드포인트가 다운되면 어떻게 되나요?
이벤트를 내구성 있게 기록한 뒤 어떤 2xx든 빠르게 반환하고, 처리는 그다음에 하세요. 전달 결과는 실행 자체를 절대 바꾸지 않습니다. 모든 시도가 거부되면 result_url에서 실행을 읽으세요.
- 재전송(Redeliver)은 실제 종료 이벤트를 새 타임스탬프와 서명으로 다시 POST합니다. 자동 시도를 모두 소진한 뒤에도 동작하며, 자동 시도 10회 중 하나를 쓰지 않습니다.
- 테스트 전송(
POST /v1/webhooks/test-deliveries,account:write)은 더미webhook.test페이로드를 보냅니다. 실제 실행을 재생하는 것이 아닙니다.
| 속성 | 실행 웹훅 | Job 웹훅 |
|---|---|---|
| 시도 횟수 | 총 최대 10회, 이후 exhausted | 총 최대 10회 |
| 간격 | min(max(30s × 2^(attempt−1) with jitter, Retry-After), 1h) | 지수 백오프가 아닌 고정 지연, 기본 30초 |
| 타임아웃 | 시도당 10초 | 시도당 10초 |
| 재전송 | Format 실행: POST /v1/format-runs/{run_id}/webhook/redeliver(formats:write 필요) | POST /v1/jobs/{job_id}/webhook/redeliver(jobs:write 필요) |
웹훅 서명 시크릿은 어떻게 교체하나요?
웹훅 → 시크릿 교체를 사용하거나, account:write가 있는 키로 POST /v1/webhooks/signing-secret/rotate를 호출하세요. 교체는 즉시 전환이 아닙니다. 교체 후 24시간 동안 Sume는 모든 전달을 두 시크릿으로 각각 서명하고, 최신 서명부터 쉼표로 구분해 x-sume-webhook-signature에 담아 보냅니다. sume-v1= 항목 중 하나라도 일치하면 전달을 수락하세요.
@sume-com/sdk 0.2.0의 verifyWebhook은 이미 다중 서명 헤더를 처리합니다. 헤더를 문자열 일치로 비교하는 자체 구현 검증기는 이 기간 동안 모든 전달에서 실패하므로, 교체하기 전에 수신기를 먼저 업그레이드하세요.
출처
관련 글
작성자 Sume