Rivya AI Docs

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:manage

key ใหม่ที่สร้างใน Settings จะมี scope นี้ตามค่าเริ่มต้น

สร้าง Endpoint

POST /api/v1/webhooks
curl 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 ส่งเสมอ:

  • POST
  • Content-Type: application/json
  • ไม่มี custom user-controlled request headers
  • ไม่ตาม automatic redirects
  • delivery timeout สั้น

Events ที่รองรับ

event types ปัจจุบัน:

  • generation.succeeded
  • generation.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-secret
curl 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-events
curl https://rivya.ai/api/v1/webhook-events \
  -H "Authorization: Bearer rvya_sk_..."

ดู delivery attempts สำหรับ endpoint:

GET /api/v1/webhooks/{endpointId}/deliveries
curl 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}/test
curl -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

หน้าที่เกี่ยวข้อง

สารบัญ