Slack 영상 생성 봇: Sume API로 만드는 슬래시 커맨드

Slack 슬래시 커맨드에 3000 ms 안에 응답하고 callback_url과 함께 POST /v1/videos를 제출한 뒤, Sume 웹훅이 오면 response_url로 URL을 게시하세요.

읽는 시간 5분Sume
전체 글

영상을 생성하는 Slack 봇을 만들려면 Slack이 허용하는 3000 ms 안에 슬래시 커맨드에 HTTP 200으로 응답한 뒤, 서버에서 callback_url과 함께 POST /v1/videos를 제출하세요. Sume의 서명된 Job 웹훅이 도착하면 media.sume.com 영상 URL을 커맨드의 response_url로 게시하고, 30분이 지났다면 chat.postMessage로 게시하세요.

Sume에는 Slack 앱이 없습니다. 여러분이 만든 Slack 앱의 서버가 HTTPS로 Sume를 직접 호출하는 방식입니다. Sume 관련 내용은 영상 생성 (영문)과 웹훅 (영문) 문서에서, Slack 관련 내용은 docs.slack.dev에서 가져왔으며 모두 2026-09-27에 확인했습니다. Sume의 서명 방식은 Sume 영상 실행용 서명된 웹훅에서 다룹니다.

봇은 왜 Sume를 호출하기 전에 Slack에 먼저 응답해야 하나요?

Slack은 슬래시 커맨드를 application/x-www-form-urlencoded 데이터를 담은 HTTP POST로 보내며, 앱이 3000밀리초 안에 HTTP 200으로 응답하지 않으면 사용자에게 operation_timeout 오류가 보입니다. 봇이 응답 전에 하는 모든 일이 이 시간을 소모하는데, 영상 자체는 훨씬 오래 걸립니다. Sume 문서에 따르면 영상 생성은 보통 30초에서 몇 분이 걸립니다. 그러니 먼저 응답하고, 그다음 제출하고, 영상이 준비되면 게시하세요.

  • 무엇보다 먼저 요청을 검증하세요. Slack은 앱의 서명 시크릿으로 v0:{timestamp}:{body}에 서명해 X-Slack-Signature로 보냅니다. 로컬 시간과 오 분 넘게 차이 나는 X-Slack-Request-Timestamp는 거부하세요.
  • 200 응답 본문에는 “Rendering your video…” 같은 메시지를 담을 수 있습니다. response_type의 기본값은 ephemeral이므로 커맨드를 실행한 사람만 이 메시지를 봅니다.
  • 나중에 제출이 실패하면 HTTP 500이 아니라 메시지로 알리세요. Slack에 따르면 상태 코드는 페이로드를 받았는지 여부만 Slack에 알려 줍니다.

봇은 영상 Job을 어떻게 시작하나요?

응답을 보낸 뒤에는 커맨드의 text를 프롬프트로 보내세요. 커맨드의 response_url과 channel_id는 반환된 Job id와 함께 저장하세요. Sume 웹훅은 Slack 대화가 아니라 Job을 가리키므로, 둘을 이어 주는 것은 여러분의 저장소입니다.

  • Idempotency-Key가 있으면 재전송 시 원래 Job이 돌아오므로 제출을 재시도해도 안전합니다. Slack의 response_url은 페이로드마다 고유하므로 그 해시는 안정적인 키가 됩니다.
  • callback_url은 공개 HTTPS URL이어야 합니다. localhost, 사설 네트워크, HTTPS가 아닌 URL은 거부됩니다.
  • 커맨드 하나하나가 워크스페이스 잔액으로 청구되는 Job을 시작하며, Sume 문서는 요청을 전달하기 전에 사용자 입력을 검증하고 자체 인가를 적용하라고 안내합니다. 받아들일 user_id나 channel_id 값을 허용 목록으로 관리하세요.
import crypto from "node:crypto";

app.post("/slack/video", express.raw({ type: "application/x-www-form-urlencoded" }), async (req, res) => {
  if (!verifySlack(req)) return res.status(401).end(); // v0 signature, 5-minute window
  const cmd = Object.fromEntries(new URLSearchParams(req.body.toString("utf8")));
  res.json({ response_type: "ephemeral", text: "Rendering your video…" }); // inside 3000 ms
  const r = await fetch("https://api.sume.com/v1/videos", {
    method: "POST",
    headers: {
      Authorization: "Bearer " + process.env.SUME_API_KEY,
      "Content-Type": "application/json",
      "Idempotency-Key": "slack-" + crypto.createHash("sha256").update(cmd.response_url).digest("hex"),
    },
    body: JSON.stringify({
      model: "sume/auto",
      prompt: cmd.text,
      callback_url: "https://bot.example.com/hooks/sume",
    }),
  });
  const job = await r.json();
  if (!r.ok) return reply(cmd.response_url, "Sume refused the job: " + job.error.code);
  await saveJob(job.id, { responseUrl: cmd.response_url, channelId: cmd.channel_id, at: Date.now() });
});

Sume 웹훅이 도착하면 영상을 어떻게 게시하나요?

Sume는 Job이 종료 상태에 도달하면 callback_url로 Job 웹훅을 한 번 POST하며, 시도마다 10초 타임아웃으로 최대 10회 시도합니다. 원본 본문으로 검증하고, 저장하고, 바로 2xx로 응답하세요. job_id를 멱등성 키로 쓰면 같은 전달이 반복되어도 두 번 게시하지 않습니다.

  • x-sume-webhook-signature는 @sume-com/sdk의 verifyWebhook으로 확인하세요. 이 함수는 시크릿 교체 중에 sume-v1= 항목 중 어느 것이든 받아들입니다. 직접 구현한 검증은 헤더를 쉼표로 나눠야 합니다.
  • job.completed에서는 type이 video인 payload.artifacts[] 항목을 가져오세요. 그 url은 media.sume.com 아래의 공개 아티팩트이므로 채널에 있는 누구나 열 수 있습니다.
  • 커맨드 후 30분 안에는 저장한 response_url로 { "response_type": "in_channel", "text": url }을 POST하세요. Slack은 그 시간 안에 이 URL로 응답을 최대 5개까지 받습니다.
  • 30분이 지난 뒤에는 `chat.postMessage`, chat:write 스코프가 있는 봇 토큰, 저장한 channel_id로 게시하세요. Slack은 일반적으로 채널당 초당 메시지 1개를 허용하며, 새 앱이 모든 공개 채널에 게시하려면 chat:write.public이 필요합니다.
  • job.failed(status: "ERROR")에서는 같은 방법으로 짧은 실패 안내를 게시하세요.

Slack 서명과 Sume 서명은 어떻게 다른가요?

봇은 들어오는 요청 두 종류를 서로 다른 시크릿 두 개로 검증합니다. 검증기는 따로 두세요.

Slack의 슬래시 커맨드·요청 검증 페이지와 Sume 웹훅 (영문) 페이지 기준, 2026-09-27 확인.
속성Slack 슬래시 커맨드Sume Job 웹훅
서명 헤더X-Slack-Signature: v0=<hex>x-sume-webhook-signature: sume-v1=<hex>
타임스탬프 헤더X-Slack-Request-Timestampx-sume-webhook-timestamp
서명 대상 문자열v0:{timestamp}:{body}{timestamp}.{raw_body}
시크릿앱 서명 시크릿워크스페이스 웹훅 서명 시크릿
재전송 허용 시간5분5분(권장 기본값)
응답 기한3000 ms시도당 10초

어떤 한도가 있나요?

두 응답 기한 말고도 봇의 동작을 좌우하는 규칙이 몇 가지 있습니다.

  • 개발자가 만든 슬래시 커맨드는 메시지 스레드에서 호출할 수 없습니다.
  • Sume는 종료 Job 이벤트만 보내며 진행 상황은 전달하지 않습니다. “still rendering”(아직 렌더링 중) 같은 업데이트가 필요하다면 GET /v1/jobs/{id}/status를 직접 폴링하세요.
  • 끝내 도착하지 않은 전달은 Job을 바꾸지 않습니다. 완료된 Job을 GET /v1/jobs/{id}/result에서 읽어 거기서 게시하세요.

출처

관련 글

연동 카테고리의 다른 글

연동 글 전체 보기

작성자 Sume