Rivya AI 文件

API Webhooks

建立已簽名的 Rivya API webhook endpoints、驗證 delivery signatures、檢查 delivery attempts,並傳送安全的 test events。

最近審閱於 2026/05/11

當你的整合需要 Rivya 在 Public API 生成到達終態後通知你的伺服器時,請使用 API webhooks。

仍然支援輪詢 GET /api/v1/generations/{taskId}。Webhooks 會為偏好事件投遞的 production systems 增加已簽名 callbacks。

所需 Scope

Webhook 管理需要具備以下 scope 的 API key:

webhooks:manage

在 Settings 中新建立的 key 預設包含此 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"]
  }'

回應只會包含一次 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 儲存在你的伺服器上。如果遺失,請呼叫 rotate endpoint 並更新你的 receiver。

URL 規則

Endpoint URLs 必須是 HTTPS。Rivya 會拒絕包含 credentials、fragments、localhost names、本機網路位址、私人 IP ranges、loopback addresses 和 reserved addresses 的 URLs。

Rivya 一律傳送:

  • POST
  • Content-Type: application/json
  • 不含自訂 user-controlled request headers
  • 不自動跟隨 redirects
  • 較短的 delivery timeout

Events

目前 event types:

  • generation.succeeded
  • generation.failed

Webhook payloads 使用與 status endpoint 相同的 public generation serializer。

{
  "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
    }
  }
}

Delivery Headers

每次 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

拒絕 stale timestamps。五分鐘 tolerance 是實務上的合理預設。

JavaScript Verification

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 Verification

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} 會停用 endpoint。它不會刪除 delivery history。

Delivery Records

列出近期 events:

GET /api/v1/webhook-events
curl https://rivya.ai/api/v1/webhook-events \
  -H "Authorization: Bearer rvya_sk_..."

列出某個 endpoint 的 delivery attempts:

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。

Test 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。它不會建立生成任務、不會消耗點數,也不會包含真實 result URL。

Retry Policy

Rivya 會將 HTTP 2xx 視為成功。

失敗包含 network errors、timeout、redirect responses 和非 2xx responses。Rivya 最多重試五次:

  • immediately
  • after 1 minute
  • after 5 minutes
  • after 30 minutes
  • after 2 hours

最終 attempt 後,event 會標記為 failed

Webhook delivery failures 不會變更 generation status、credits、refunds 或 task history。

安全清單

  • 在解析 business logic 前驗證 HMAC signature。
  • 拒絕 stale timestamps。
  • 將 test events 與 generation events 分開處理。
  • 不要記錄完整 signing secrets。
  • 只有在你的 receiver 已接受 event 後才回傳 2xx
  • 保留 polling 作為 reconciliation fallback。

相關頁面

目錄