API Webhooks ของ Rivya
สร้าง Rivya API webhook endpoints แบบ signed, ตรวจ delivery signatures, ดู delivery attempts และส่ง test events ที่ปลอดภัย
ตรวจล่าสุดเมื่อ 2026/05/11
ใช้ API webhooks เมื่อ integration ของคุณต้องการให้ Rivya แจ้ง server ของคุณหลัง Public API generation เข้าสู่ terminal state
การ polling GET /api/v1/generations/{taskId} ยังคงรองรับอยู่ Webhooks เพิ่ม signed callbacks สำหรับ production systems ที่ต้องการ event delivery
Scope ที่จำเป็น
การจัดการ webhook ต้องใช้ API key ที่มี:
webhooks:managekey ใหม่ที่สร้างใน Settings จะมี scope นี้ตามค่าเริ่มต้น
สร้าง Endpoint
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"]
}'response มี 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
}เก็บ secret เต็มไว้บน server ของคุณ หากทำหาย ให้เรียก rotate endpoint แล้วอัปเดต receiver ของคุณ
กฎ URL
Endpoint URLs ต้องเป็น HTTPS Rivya จะปฏิเสธ URLs ที่มี credentials, fragments, localhost names, local network addresses, private IP ranges, loopback addresses และ reserved addresses
Rivya ส่งเสมอ:
POSTContent-Type: application/jsonไม่มี custom user-controlled request headers
ไม่ตาม automatic redirects
delivery timeout สั้น
Events ที่รองรับ
event types ปัจจุบัน:
generation.succeededgeneration.failed
Webhook payloads ใช้ public generation serializer ชุดเดียวกับ status endpoint
{
"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
}
}
}Header สำหรับ Delivery
แต่ละ delivery มี:
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_...signature input:
${timestamp}.${rawBody}algorithm:
HMAC-SHA256 with the endpoint signing secretปฏิเสธ requests ที่ timestamp เก่าเกินไป ค่า tolerance ห้านาทีเป็นค่า default ที่ใช้งานได้จริง
การตรวจยืนยันด้วย 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)จัดการ Endpoints
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} จะ disable endpoint แต่ไม่ลบ delivery history
บันทึก Delivery
ดู recent events:
GET /api/v1/webhook-eventscurl https://rivya.ai/api/v1/webhook-events \
-H "Authorization: Bearer rvya_sk_..."ดู delivery attempts สำหรับ endpoint:
GET /api/v1/webhooks/{endpointId}/deliveriescurl https://rivya.ai/api/v1/webhooks/whend_.../deliveries \
-H "Authorization: Bearer rvya_sk_..."delivery records มี status, HTTP status, attempt number, request id, duration, response snippet ที่ตัดให้สั้น และ public error fields
Event ทดสอบ
ส่ง test payload ที่ปลอดภัย:
POST /api/v1/webhooks/{endpointId}/testcurl -X POST https://rivya.ai/api/v1/webhooks/whend_.../test \
-H "Authorization: Bearer rvya_sk_..."test event ใช้ webhook.test โดยไม่สร้าง generation task, ไม่ใช้ credits และไม่มี result URL จริง
นโยบาย Retry
Rivya ถือ HTTP 2xx เป็น success
failures รวม network errors, timeout, redirect responses และ non-2xx responses Rivya retry ได้สูงสุดห้าครั้ง:
ทันที
หลัง 1 นาที
หลัง 5 นาที
หลัง 30 นาที
หลัง 2 ชั่วโมง
หลัง attempt สุดท้าย event จะถูกทำเครื่องหมายเป็น failed
Webhook delivery failures ไม่เปลี่ยน generation status, credits, refunds หรือ task history
checklist ความปลอดภัย
ตรวจ HMAC signature ก่อน parse business logic
ปฏิเสธ timestamps ที่เก่าเกินไป
แยก test events ออกจาก generation events
อย่าบันทึก full signing secrets ลง log
คืน
2xxหลัง receiver ของคุณยอมรับ event แล้วเท่านั้นเก็บ polling เป็น fallback สำหรับ reconciliation