본문으로 건너뛰기
가이드
가이드

콜백

Task 콜백 전달을 안전하게 수신하고 확인합니다.

공개 HTTPS 엔드포인트에서 Task 수명 주기 이벤트를 수신하려면 콜백을 사용하세요. JSON 본문을 처리하기 전에 모든 콜백을 검증하여 계정에 서명된 전달만이 애플리케이션 상태를 변경할 수 있도록 하세요.

콜백 URL 구성

Task를 생성할 때 공개 HTTPS callback_url을 추가합니다. 이벤트 본문과 생명주기 상태는 엔드포인트에 따라 다르며, 콜백 페이로드에 대한 자세한 내용은 해당 엔드포인트의 API 참조를 확인하십시오.

JSON
{
  "model": "flux-2-pro-text-to-image",
  "prompt": "A product photograph on a clean studio background",
  "callback_url": "https://your-domain.com/webhooks/runapi"
}

Callback Secret 만들기

인증 가이드에 따라 로그인한 후 API 키를 열고 Task를 생성하는 계정에 대한 콜백 시크릿을 생성하세요. 값을 시크릿 매니저에 저장하고 콜백 수신기에서만 사용할 수 있도록 하세요. 이것은 API 키가 아니며 Task 요청에 절대 전송해서는 안 됩니다.

비밀은 해당 계정의 콜백에 서명합니다. 비밀을 교체하면 이후 전달의 서명이 변경되므로, 모든 콜백 수신자를 즉시 업데이트하고, 진행 중인 전달을 처리하는 데 필요한 시간 동안만 두 값을 모두 사용할 수 있는 상태로 유지하세요.

콜백 서명 확인

각 콜백은 Content-Type: application/json을 가진 HTTP POST입니다. RunAPI는 이 요청에 Authorization 헤더를 추가하지 않습니다. 본문을 역직렬화하기 전에 다음 헤더를 확인하세요:

헤더 의미 X-Callback-Id 이번 전송 시도에 대한 고유 식별자. X-Callback-Timestamp 전송이 서명된 시점의 Unix 타임스탬프(초 단위). X-Callback-Signature Base64로 인코딩된 HMAC-SHA-256 서명.

수정되지 않은 요청 본문 바이트를 사용하여 다음과 같이 정확하게 서명 값을 작성하십시오:

TEXT
X-Callback-Id + "." + X-Callback-Timestamp + "." + raw request body

Callback Secret을 Base64로 디코딩하고, 해당 값에 대해 HMAC-SHA-256을 계산한 후 결과를 Base64로 인코딩하여 타이밍 안전 비교 방식으로 X-Callback-Signature와 비교하십시오. 검증 전에 JSON을 파싱하거나 재직렬화하지 마십시오.

JavaScript

JAVASCRIPT
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyCallback({headers, rawBody, callbackSecret}) {
  const callbackId = headers["x-callback-id"];
  const timestamp = Number(headers["x-callback-timestamp"]);
  const signature = headers["x-callback-signature"];

  if (!callbackId || !signature || !Number.isSafeInteger(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const signedContent = `${callbackId}.${timestamp}.${rawBody}`;
  const expected = createHmac("sha256", Buffer.from(callbackSecret, "base64"))
    .update(signedContent, "utf8")
    .digest();
  const received = Buffer.from(signature, "base64");

  return expected.length === received.length && timingSafeEqual(expected, received);
}

프레임워크의 원시 요청 본문 문자열을 rawBody에 전달하세요. 파싱된 데이터에 JSON.stringify를 호출하지 마세요.

Python

PYTHON
import base64
import hashlib
import hmac
import time


def verify_callback(headers, raw_body, callback_secret):
    callback_id = headers.get("X-Callback-Id")
    timestamp = headers.get("X-Callback-Timestamp")
    signature = headers.get("X-Callback-Signature")

    if not callback_id or not timestamp or not signature:
        return False

    try:
        timestamp = int(timestamp)
        secret = base64.b64decode(callback_secret, validate=True)
        received = base64.b64decode(signature, validate=True)
    except (ValueError, TypeError):
        return False

    if abs(time.time() - timestamp) > 300:
        return False

    signed_content = f"{callback_id}.{timestamp}.{raw_body}".encode("utf-8")
    expected = hmac.new(secret, signed_content, hashlib.sha256).digest()
    return hmac.compare_digest(expected, received)

HTTP 프레임워크가 수신한 정확한 원시 요청 본문 문자열을 raw_body로 전달하세요.

안전한 전달 처리

  • 콜백이 처리 대기 중으로 수락된 후에만 2xx 응답을 반환하십시오. 2xx가 아닌 응답이나 전송 실패 후에는 최대 10회까지 재전송을 시도합니다.
  • 15초 이내에 응답하십시오. 처리 속도가 느린 작업은 HTTP 응답을 차단하는 대신 검증 후 큐에 넣으십시오.
  • 서명 헤더가 누락되었거나, 서명이 유효하지 않거나, 타임스탬프가 수신기에서 정의한 허용 범위를 벗어난 콜백은 거부하십시오.

검증 문제 해결

  • 서명 불일치: Callback Secret이 작업과 동일한 계정에 속하는지 확인하고, Base64로 디코딩한 후 파싱된 JSON이 아닌 원본 바디에 서명하세요.
  • 서명 헤더 누락: 상태 변경에 콜백을 사용하기 전에 Callback Secret을 생성하세요.
  • 타임스탬프 거부됨: 수신자의 클럭을 동기화하고 배포 환경에 적합한 허용 오차를 사용하세요.

설정 및 수명 주기 안내에 대해서는 Task API 빠른 시작으로 돌아가세요.