Rivya AI 文档

API Webhooks

创建 Rivya API 签名 webhook endpoint,校验投递签名,查看投递记录,并发送安全测试事件。

最近审阅于 2026/05/11

当你的集成希望在 Public API 生成任务进入终态后由 Rivya 主动通知你的服务端,可以使用 API webhooks。

GET /api/v1/generations/{taskId} 轮询仍然可用。Webhook 适合希望通过事件投递接收结果的生产系统。

所需 Scope

Webhook 管理需要 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 保存到你的服务端。如果丢失,只能轮换并更新接收端配置。

URL 规则

Endpoint URL 必须是 HTTPS。Rivya 会拒绝带用户名密码、fragment、localhost、局域网地址、私有 IP、环回地址和保留地址的 URL。

Rivya 固定发送:

  • POST
  • Content-Type: application/json
  • 不允许用户自定义请求头
  • 不自动跟随 redirect
  • 较短投递 timeout

事件类型

当前事件类型:

  • generation.succeeded
  • generation.failed

Webhook payload 使用和任务状态接口一致的公共 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
    }
  }
}

投递 Headers

每次投递包含:

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 with the endpoint signing secret

建议拒绝时间戳过旧的请求,常用窗口是 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)

管理 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,不会删除历史投递记录。

投递记录

查看最近事件:

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

查看某个 endpoint 的投递尝试:

GET /api/v1/webhooks/{endpointId}/deliveries
curl https://rivya.ai/api/v1/webhooks/whend_.../deliveries \
  -H "Authorization: Bearer rvya_sk_..."

投递记录包含 status、HTTP status、attempt number、request id、duration、截断后的 response snippet 和公共错误字段。

测试事件

发送安全测试 payload:

POST /api/v1/webhooks/{endpointId}/test
curl -X POST https://rivya.ai/api/v1/webhooks/whend_.../test \
  -H "Authorization: Bearer rvya_sk_..."

测试事件类型是 webhook.test。它不会创建生成任务,不会消耗积分,也不会包含真实结果 URL。

重试策略

Rivya 把 HTTP 2xx 视为成功。

失败包含网络错误、timeout、redirect 响应和非 2xx 响应。Rivya 最多尝试 5 次:

  • 立即
  • 1 分钟后
  • 5 分钟后
  • 30 分钟后
  • 2 小时后

最后一次仍失败时,event 标记为 failed

Webhook 投递失败不会改变生成任务状态、积分、退款或任务历史。

安全清单

  • 在处理业务逻辑前先校验 HMAC 签名。
  • 拒绝时间戳过旧的请求。
  • 区分测试事件和生成事件。
  • 不记录完整 signing secret。
  • 只有在接收端确实接受事件后才返回 2xx
  • 保留轮询作为对账 fallback。

相关页面

目录