API
CLUB

事件回调

注册一个 HTTPS 接口后,Uika 会在媒体任务结束时立即发送带签名的事件,无需轮询。回调签名遵循 Standard Webhooks 规范,兼容该规范的库均可验证回调。

管理回调接口

注册回调接口

每个组织最多 16 个

在本控制台的“事件回调”页添加接口。所有者和管理员可以新增、编辑和删除,所有成员均可查看。新接口默认接收全部事件类型,包括将来新增的类型;如只需部分事件,请设置事件筛选。

  • 必须使用 443 端口的 HTTPS,且公网可以访问该地址。
  • 不接受 pika.art 域名下的地址。
  • 请立即返回 2xx 响应,再异步处理耗时工作。
  • 系统不会跟随重定向,请填写最终地址。
事件触发时机
media.job.completed任务成功完成,结果已可获取。
media.job.failed任务失败,错误代码见 data.error。

在接口详情页点击“发送测试”,会发送带签名的 webhook.test 事件。无需运行任务,即可检查地址、签名和处理逻辑。无论事件筛选如何设置,接口都会收到测试事件;测试仅发送一次,不会重试。

单次请求回调

一次回调,对应一个任务

提交媒体任务时,可以选填 webhook_url。Uika 会将该任务的最终状态事件发送到此地址,无需预先注册接口。

提交时指定回调
POST https://api.dev.pika.art/v1/media/pika/pika-2.5/text-to-video
authorization: Bearer $UIKA_API_KEY
content-type: application/json
{
"prompt": "a paper boat crossing a puddle",
"webhook_url": "https://example.com/pika/webhooks"
}

此类回调使用组织级单次请求密钥签名,而非某个接口的密钥。可在控制台的“事件回调”页查看,或通过以下接口获取: GET /v1/webhooks/secret。其他规则一致,包括事件结构、 webhook-* 请求头、Standard Webhooks 验证和重试计划。

  • 地址需满足与注册接口相同的要求:HTTPS、443 端口、公网可访问、非 pika.art 域名,且不能包含用户名或密码。
  • 回调可能早于提交请求的响应到达。请通过 data.id 匹配任务,并使用 webhook-id 去重。
  • 重复使用幂等键时,如果 webhook_url 不同,会返回 409。
  • 任务失败时,也会向同一地址发送 media.job.failed,无需额外轮询失败状态。

回调内容示例

POST · application/json

回调投递
POST https://example.com/pika/webhooks
content-type: application/json
user-agent: pika-webhooks/1.0
webhook-id: msg_0f1e4b7a-9c33-4d81-b0a2-6e5d8c7f41ab
webhook-timestamp: 1786650067
webhook-signature: v1,LWSb0oRhsMhAaMLpQGXjNL0aOEmMPuTv0AUKZBFPEUE=
{
"type": "media.job.completed",
"timestamp": "2026-08-14T09:41:07.482913+00:00",
"data": {
"id": "media_8f3a2c91-5b7d-4e0a-9c26-31d4f2a8e6b0",
"status": "completed",
"model_id": "pika/pika-2.5/text-to-video",
"runtime_s": 42.7,
"output": {
"media_type": "video",
"video": {
"url": "https://api.dev.pika.art/v1/files/output.mp4",
"content_type": "video/mp4"
}
},
"error": null,
"usage": { "video_output_tokens": 50638 },
"billing": null
}
}
  • webhook-id 是事件标识,重试与重放会复用此值,收到重复 ID 时可直接去重。
  • webhook-timestamp 是本次发送尝试的 Unix 时间,而非事件发生时间。
  • data 是任务完成时的任务对象,结构与 GET /v1/media/jobs/{request_id} 的返回一致。
  • runtime_s 和 usage 会尽量提供,但也可能为 null。
  • 回调中的 billing 始终为 null。事件进入队列后才结算费用,请查询 GET /v1/media/jobs/{request_id},待 billing.state 为 settled 后读取最终费用。

验证签名

验证通过后,再信任回调内容

向 Standard Webhooks 库传入原始请求字节及三个 webhook-* 请求头。务必使用原始字节,重新序列化后的 JSON 会导致签名不匹配。

from fastapi import FastAPI, HTTPException, Request
from standardwebhooks import Webhook, WebhookVerificationError
app = FastAPI()
webhook = Webhook(UIKA_WEBHOOK_SECRET)
@app.post("/pika/webhooks")
async def receive(request: Request) -> dict:
try:
event = webhook.verify(await request.body(), dict(request.headers))
except WebhookVerificationError:
raise HTTPException(status_code=400, detail="invalid signature")
handle(event["type"], event["data"])
return {"ok": True}
  • 验证库会拒绝时间偏差超过五分钟的请求,以防攻击者重放此前捕获的回调。
  • 密钥轮换期间,请求头包含多个签名。匹配任意一个即有效,标准库已支持此行为。
  • 验证失败请返回 4xx 并停止处理,不要读取正文。

不使用库时的验证方式

签名原文是以下字节序列: {webhook-id}.{webhook-timestamp}.{raw body}。获取密钥时,先去掉 whsec_ 前缀,再对剩余部分进行 Base64 解码。使用该密钥对签名原文计算 HMAC-SHA256,再对结果进行 Base64 编码。

该 webhook-signature 请求头包含一个或多个 v1,<signature> 值,以空格分隔。请以恒定时间分别比较,任意匹配即可接受回调。这样轮换密钥时不会漏掉事件。同时请检查 webhook-timestamp 是否在允许的时间范围内。通常允许五分钟,以防重放旧请求。

import base64
import hashlib
import hmac
import time
def verify(secret, body, msg_id, timestamp, signature_header):
if abs(time.time() - int(timestamp)) > 300:
raise ValueError("timestamp outside tolerance")
key = base64.b64decode(secret.removeprefix("whsec_"))
signed = f"{msg_id}.{timestamp}.".encode() + body
expected = base64.b64encode(
hmac.new(key, signed, hashlib.sha256).digest()
).decode()
for part in signature_header.split(" "):
version, _, signature = part.partition(",")
if version == "v1" and hmac.compare_digest(expected, signature):
return
raise ValueError("no matching signature")

重试

约 55 小时内最多尝试 9 次

接口在 20 秒内返回任意 2xx 即视为成功;其他情况均视为失败,并按计划重试。

  • 重试间隔约为 5 秒、5 分钟、30 分钟、2 小时、5 小时、10 小时、14 小时和 24 小时,并加入随机偏移。
  • 以秒为单位的 Retry-After 请求头可推迟下次重试,最长推迟一小时,但不能让重试提前。
  • 返回 410 Gone 时,系统会停用该接口,并显示“已停用(Gone)”;你可在接口页重新启用。
  • 最后一次尝试仍失败时,回调会标记为失败,你可在接口详情页手动重放。
  • 每次尝试均复用同一个 webhook-id,请以此去重。
  • webhook.test 事件例外:只尝试一次,不会重试。

每次失败都会记录原因,你可在控制台的接口详情页查看近期回调及所有尝试记录。

错误含义
timeout20 秒内未收到响应。
connect无法建立连接。
blocked出口网络拒绝该地址,原因可能是私有地址或未获允许。
tlsTLS 握手或证书校验失败。
http_4xx:<代码>你的接口返回了 4xx。
http_5xx:<代码>你的接口返回了 5xx。
signing回调签名失败,系统会重试,不收取费用。

密钥与轮换

平滑轮换,不遗漏事件

每个接口有独立签名密钥,单次请求回调则共用组织级密钥。两种密钥均为 Base64 编码,并带有 whsec_ 前缀。接口密钥可在详情页查看,组织密钥可在“事件回调”页查看。两者轮换方式相同:选择旧密钥立即失效,或继续有效一小时、一天。

在重叠有效期内,每次回调都会用新旧密钥分别签名,因此 webhook-signature 包含两个以空格分隔的值,任意签名均有效,可在不中断事件的情况下切换。

密钥轮换期间
webhook-signature: v1,LWSb0oRhsMhAaMLpQGXjNL0aOEmMPuTv0AUKZBFPEUE= v1,tQ9nS2pKcW4Hs1dRz7yVbXm0gJ3FfN8uLoEiA6TrCxU=