API 웹훅
서명된 Rivya API 웹훅 엔드포인트를 만들고 서명을 검증하며, 전달 시도 기록을 확인하고 안전한 테스트 이벤트를 보내는 방법을 알아보세요.
최근 검토일 2026년 5월 11일
Public API 생성 작업이 최종 상태에 도달했을 때 Rivya가 서버로 결과를 알려야 하는 연동에는 API 웹훅을 사용하세요.
GET /api/v1/generations/{taskId}를 이용한 상태 조회도 계속 지원됩니다. 웹훅은 이벤트를 서버로 전달받아 처리하려는 운영 환경에 서명된 콜백을 제공합니다.
필요한 권한 범위
웹훅을 관리하려면 다음 권한 범위가 있는 API 키가 필요합니다.
webhooks:manage설정에서 새로 만든 키에는 이 권한 범위가 기본으로 포함됩니다.
엔드포인트 만들기
POST /api/v1/webhookscurl https://rivya.ai/api/v1/webhooks \
-H "Authorization: Bearer rvya_sk_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Production webhook",
"url": "https://example.com/rivya/webhook",
"event_types": ["generation.succeeded", "generation.failed"]
}'응답에는 signing_secret이 한 번만 포함됩니다.
{
"id": "whend_...",
"object": "webhook_endpoint",
"name": "Production webhook",
"url": "https://example.com/rivya/webhook",
"event_types": ["generation.succeeded", "generation.failed"],
"status": "active",
"secret_preview": "whsec_12...abc123",
"signing_secret": "whsec_...",
"last_success_at": null,
"last_failure_at": null,
"failure_count": 0,
"created_at": "2026-05-11T00:00:00.000Z",
"updated_at": "2026-05-11T00:00:00.000Z",
"disabled_at": null,
"revoked_at": null
}전체 비밀 키는 서버에 안전하게 저장하세요. 잃어버렸다면 비밀 키 교체 엔드포인트를 호출한 뒤 수신 서버의 설정도 업데이트해야 합니다.
URL 규칙
엔드포인트 URL은 HTTPS를 사용해야 합니다. Rivya는 인증 정보나 프래그먼트가 포함된 URL, localhost 이름, 로컬 네트워크 주소, 사설 IP 대역, 루프백 주소와 예약 주소를 거부합니다.
Rivya는 항상 다음을 보냅니다.
POSTContent-Type: application/json사용자가 지정하는 별도 요청 헤더 없음
리디렉션 자동 추적 없음
짧은 전달 제한 시간
이벤트
현재 지원하는 이벤트 유형은 다음과 같습니다.
generation.succeededgeneration.failed
웹훅 페이로드는 상태 확인 엔드포인트와 같은 공개 생성 응답 형식을 사용합니다.
{
"id": "evt_...",
"type": "generation.succeeded",
"api_version": "2026-05-11",
"created_at": "2026-05-11T00:00:00.000Z",
"data": {
"generation": {
"id": "task_public_id",
"status": "succeeded",
"model": "z-image",
"reserved_credits": 1,
"final_credits": 1,
"created_at": "2026-05-11T00:00:00.000Z",
"updated_at": "2026-05-11T00:01:00.000Z",
"result": {
"primary_url": "https://...",
"urls": ["https://..."]
},
"error": null
}
}
}전달 헤더
각 전달 요청에는 다음 헤더가 포함됩니다.
Rivya-Webhook-Id: evt_...
Rivya-Webhook-Timestamp: 1778467200
Rivya-Webhook-Signature: v1=<hex-hmac-sha256>
Rivya-Webhook-Attempt: 1
Rivya-Webhook-Endpoint-Id: whend_...
Rivya-Request-Id: req_...서명 입력값:
${timestamp}.${rawBody}알고리즘:
엔드포인트 서명용 비밀 키를 사용하는 HMAC-SHA256오래된 타임스탬프가 포함된 요청은 거부하세요. 허용 시간 차이는 5분이 실용적인 기본값입니다.
JavaScript 검증 예시
import crypto from "node:crypto";
function verifyRivyaWebhook({ rawBody, headers, signingSecret }) {
const timestamp = headers["rivya-webhook-timestamp"];
const signature = headers["rivya-webhook-signature"] || "";
const actual = signature.split(",").find((part) => part.startsWith("v1="))?.slice(3);
const expected = crypto
.createHmac("sha256", signingSecret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
if (!actual) return false;
return crypto.timingSafeEqual(Buffer.from(actual, "hex"), Buffer.from(expected, "hex"));
}Python 검증 예시
import hmac
import hashlib
def verify_rivya_webhook(raw_body: str, headers: dict, signing_secret: str) -> bool:
timestamp = headers.get("rivya-webhook-timestamp", "")
signature = headers.get("rivya-webhook-signature", "")
actual = next((part[3:] for part in signature.split(",") if part.startswith("v1=")), "")
expected = hmac.new(
signing_secret.encode(),
f"{timestamp}.{raw_body}".encode(),
hashlib.sha256,
).hexdigest()
return bool(actual) and hmac.compare_digest(actual, expected)엔드포인트 관리
GET /api/v1/webhooks
GET /api/v1/webhooks/{endpointId}
PATCH /api/v1/webhooks/{endpointId}
DELETE /api/v1/webhooks/{endpointId}
POST /api/v1/webhooks/{endpointId}/rotate-secretcurl https://rivya.ai/api/v1/webhooks \
-H "Authorization: Bearer rvya_sk_..."
curl https://rivya.ai/api/v1/webhooks/whend_... \
-H "Authorization: Bearer rvya_sk_..."
curl -X PATCH https://rivya.ai/api/v1/webhooks/whend_... \
-H "Authorization: Bearer rvya_sk_..." \
-H "Content-Type: application/json" \
-d '{"status":"disabled"}'
curl -X POST https://rivya.ai/api/v1/webhooks/whend_.../rotate-secret \
-H "Authorization: Bearer rvya_sk_..."DELETE /api/v1/webhooks/{endpointId}는 엔드포인트를 비활성화하지만 전달 기록은 삭제하지 않습니다.
전달 기록
최근 이벤트 조회:
GET /api/v1/webhook-eventscurl https://rivya.ai/api/v1/webhook-events \
-H "Authorization: Bearer rvya_sk_..."엔드포인트의 전달 시도 조회:
GET /api/v1/webhooks/{endpointId}/deliveriescurl https://rivya.ai/api/v1/webhooks/whend_.../deliveries \
-H "Authorization: Bearer rvya_sk_..."전달 기록에는 처리 상태, HTTP 상태, 시도 횟수, 요청 ID, 처리 시간, 길이가 제한된 응답 일부와 공개 오류 필드가 포함됩니다.
테스트 이벤트
안전한 테스트 페이로드 보내기:
POST /api/v1/webhooks/{endpointId}/testcurl -X POST https://rivya.ai/api/v1/webhooks/whend_.../test \
-H "Authorization: Bearer rvya_sk_..."테스트 이벤트는 webhook.test 유형을 사용합니다. 생성 작업을 만들거나 크레딧을 사용하지 않으며 실제 결과 URL도 포함하지 않습니다.
재시도 정책
Rivya는 HTTP 2xx 응답을 성공으로 처리합니다.
네트워크 오류, 제한 시간 초과, 리디렉션 응답과 2xx가 아닌 응답은 실패로 처리됩니다. Rivya는 최대 다섯 번까지 전달을 시도합니다.
즉시
1분 후
5분 후
30분 후
2시간 후
마지막 시도까지 실패하면 이벤트가 failed로 표시됩니다.
웹훅 전달 실패는 생성 상태, 크레딧, 환불 또는 작업 기록을 바꾸지 않습니다.
보안 체크리스트
요청을 해석해 업무 로직을 실행하기 전에 HMAC 서명을 검증하세요.
오래된 타임스탬프를 거부하세요.
테스트 이벤트와 생성 이벤트를 구분해 처리하세요.
전체 서명용 비밀 키를 로그에 남기지 마세요.
수신 서버가 이벤트를 정상적으로 받아들인 뒤에만
2xx를 반환하세요.누락 여부를 대조할 수 있도록 상태 조회 방식도 함께 유지하세요.
