Trigger.dev 웹훅 대기: Sume 실행용 waitpoint 토큰

Trigger.dev waitpoint 토큰을 만들고 token.url을 Sume 실행의 webhook_url로 보내면, Sume가 실행 결과를 POST할 때 wait.forToken()이 반환됩니다.

읽는 시간 5분Sume
전체 글

Trigger.dev 태스크가 Sume 웹훅을 기다리게 하려면, wait.createToken()으로 waitpoint 토큰을 만들고 token.url을 실행의 communication.webhook_url로 보낸 뒤 wait.forToken()을 호출하세요. 실행이 완료되거나 실패하면 Sume가 그 URL로 영수증을 한 번 POST하고, 그 JSON 본문이 토큰의 출력이 됩니다.

Sume에는 Trigger.dev 전용 연동이 없으므로, 태스크가 fetch로 API를 직접 호출합니다. Sume 관련 사실은 Format 호출하기 (영문), 실행과 결과 (영문), Run 웹훅 (영문)에서, Trigger.dev 동작은 2026-09-27에 확인한 Trigger.dev 문서에서 가져왔습니다. 웹훅 계약은 Sume Format 실행 수명주기에서 다룹니다.

waitpoint 토큰은 Sume 실행과 어떻게 대응하나요?

토큰 규칙마다 여러분이 시작하는 실행에 미치는 결과가 있습니다.

Trigger.dev 토큰 대기, Wait 문서와 Sume 실행과 결과 (영문) 기준, 2026-09-27 확인.
Trigger.devSume 쪽
wait.createToken({ timeout })은 id와 url이 있는 토큰을 반환합니다. timeout의 기본값은 "10m"입니다.실행의 90분 상한을 넘게 설정합니다.
token.url로 POST하면 토큰이 완료되고, 그 JSON 본문이 출력이 됩니다.Sume는 실행이 완료되거나 실패할 때 종료 영수증을 한 번 POST합니다.
wait.forToken()은 { ok, output, error }를 반환하며, 발생할 수 있는 오류는 타임아웃뿐입니다.취소되거나 건너뛴 실행은 POST하지 않으므로 그 토큰은 타임아웃됩니다.
createToken에 같은 idempotencyKey를 다시 쓰면 캐시된 토큰(isCached: true)이 반환됩니다.재시도해도 같은 token.url을 받으므로 Sume 요청 본문도 그대로입니다.
Trigger.dev Cloud는 몇 초 넘게 기다리는 태스크를 일시 중지합니다.영상 실행은 몇 분이 걸립니다.

태스크는 어떤 모습인가요?

키는 Trigger.dev 환경 변수로 저장하되 만들 때 Secret으로 표시하고, process.env에서 읽으세요. 태스크는 토큰을 만들고, 토큰 URL로 카탈로그 Format 실행을 시작하고, 기다린 다음, 자신의 키로 실행을 읽습니다.

import { task, wait } from "@trigger.dev/sdk";
export const productVideo = task({
  id: "product-video",
  run: async (payload: { orderId: string; photoUrl: string }) => {
    const auth = { Authorization: `Bearer ${process.env.SUME_API_KEY}` };
    const token = await wait.createToken({
      timeout: "2h", idempotencyKeyTTL: "3h",
      idempotencyKey: `sume-${payload.orderId}-v1`,
    });
    const res = await fetch("https://api.sume.com/v1/formats/sume/sume-product-commercial/runs", {
      method: "POST",
      headers: { ...auth, "Content-Type": "application/json", "Idempotency-Key": `order-${payload.orderId}-v1` },
      body: JSON.stringify({
        instruction: "Make a vertical product commercial from the attached photo.",
        attachments: [{ type: "input_image", image_url: payload.photoUrl }],
        communication: { webhook_url: token.url },
      }),
    });
    const created = await res.json();
    if (!res.ok) throw new Error(`Sume ${res.status} ${created.error?.code}`);
    const result = await wait.forToken(token); // ok is false only on a timeout
    const run = await fetch(`https://api.sume.com/v1/format-runs/${created.data.id}`, { headers: auth });
    return { delivered: result.ok, run: (await run.json()).data };
  },
});

토큰은 얼마나 기다려야 하나요?

기본값인 10분보다 길어야 합니다. 롱폼 호스트 영상은 보통 15~30분이면 끝납니다. Sume는 created_at으로부터 90분이 지난 실행을 failed로 강제 종료하므로, 두 시간 타임아웃이면 스스로 끝나는 모든 실행을 기다릴 수 있습니다. 여러분의 타임아웃은 실행을 취소하지 않습니다. 실행은 계속 진행되며 계속 과금되고, 그래서 태스크는 타임아웃 뒤에도 실행을 읽습니다.

태스크는 토큰의 출력을 믿어도 되나요?

그것만으로는 믿을 수 없습니다. 출력은 POST의 JSON 본문인 반면 Sume의 HMAC 서명은 x-sume-webhook-signature 헤더에 실려 오므로, 태스크는 출력에서 서명을 확인할 수 없습니다. 토큰은 결과를 확인하러 가라는 신호로 보고, 위 코드처럼 여러분의 키로 GET /v1/format-runs/{run_id}를 읽으세요. 그 data가 웹훅에 담겼던 것과 같은 영수증입니다. 이 읽기는 payload: null로 도착하는, 1 MiB를 넘는 영수증도 처리합니다.

재시도할 때 두 번째 유료 실행은 어떻게 피하나요?

Trigger.dev는 태스크가 예외를 던지면 태스크를 재시도하며, 재시도는 토큰과 실행을 다시 만듭니다. 멱등성 키 두 개와 습관 하나가 이를 안전하게 만듭니다.

  • Sume Idempotency-Key: 같은 키와 본문이면 원래 실행과 함께 200이 돌아오고 두 번째 청구는 없습니다.
  • 토큰의 idempotencyKey: 재시도하면 캐시된 토큰과 그 URL을 받으므로 Sume 본문이 정말로 같아집니다. 현재 코드에서는 Sume 키를 확인할 때 비교하는 본문에 웹훅 URL도 포함되므로, 같은 키에 새 URL을 보내면 409 idempotency_conflict가 돌아옵니다. idempotencyKeyTTL의 기본값은 1시간이므로 전체 대기 시간보다 길게 설정하세요.
  • .unwrap()을 호출하지 말고 result.ok를 확인하세요. .unwrap()은 타임아웃이 나면 예외를 던져 태스크를 재시도로 보냅니다.

출처

관련 글

연동 카테고리의 다른 글

연동 글 전체 보기

작성자 Sume