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/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
}请把完整 secret 保存到你的服务端。如果丢失,只能轮换并更新接收端配置。
URL 规则
Endpoint URL 必须是 HTTPS。Rivya 会拒绝带用户名密码、fragment、localhost、局域网地址、私有 IP、环回地址和保留地址的 URL。
Rivya 固定发送:
POSTContent-Type: application/json- 不允许用户自定义请求头
- 不自动跟随 redirect
- 较短投递 timeout
事件类型
当前事件类型:
generation.succeededgeneration.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-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} 会禁用 endpoint,不会删除历史投递记录。
投递记录
查看最近事件:
GET /api/v1/webhook-eventscurl https://rivya.ai/api/v1/webhook-events \
-H "Authorization: Bearer rvya_sk_..."查看某个 endpoint 的投递尝试:
GET /api/v1/webhooks/{endpointId}/deliveriescurl 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}/testcurl -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。