API:令牌与错误
用 API,你们的代码可以上传实验和追踪,CI 可以发起一次运行并等它的发布门槛,脚本可以读网页上能看到的东西。本页说明令牌、请求怎么认证、CI 用的 /v1 接口,以及 API 怎么返回错误。/v2 的全部接口(数据集、评分器、实验、门槛、追踪、在线评分、告警、Playground、人工评审)见 v2 接口;OTLP 和 Gateway 的地址见 OpenTelemetry 与 AI Gateway。
接口在 https://<平台地址>/v1/ 和 /v2/ 下,收发 JSON,要在请求头 Authorization: Bearer <令牌> 里带令牌。
令牌
| 令牌 | 开头 | 怎么得到 | 能做什么 |
|---|---|---|---|
| 项目 API 令牌 | pj_ | 项目的「自动化 → API 令牌」 | 发起这一个项目的运行,读它的任务和运行;读它的数据集、评分器、实验、门槛和追踪。勾了**「允许上传日志与实验」**(ingest 权限)的,还能建数据集、行、版本和实验,上传实验行和追踪,调用 OTLP 和 Gateway。它从不能改评分器、发布门槛、在线评分规则或告警,不能改数据集名称或删行,也不能用 Playground 和人工评审。 |
| CLI 令牌 | rt_ | reviewctl login(见命令行) | 读你在网页上能看的东西,上传运行。不能发起运行。 |
| 开发机 AI 的连接令牌 | dl_ | reviewctl dev connect 用配对码换来,按仓库存在 ~/.reviewctl/dev-links/ 下;或者在「设置 → 开发机 AI」选 MCP over HTTP 时,页面显示一次 | 只调开发机 AI 的接口(/v1/dev/*),只在建它的那个项目里,能做的按这个连接勾的权限。30 天有效。 |
| 测试机令牌 | rn_ | reviewctl runner register 用配对码换来,存在 ~/.reviewctl/runners/<项目编号>.json | 只给本地测试机自己用:领任务、上报心跳和结果。不能调别的接口。 |
这几种令牌都以建它(或配对它)的人的身份行事。用令牌发起的运行算这个人的,和这个人手动发起的一样计费。令牌被撤销,或者这个人离开组织,令牌就失效。项目 API 令牌在项目删除后也会失效。
建一个项目 API 令牌
- 打开项目,进入「自动化」。
- 在「API 令牌」下填「名称」(比如
GitHub Actions)。给 SDK、OpenTelemetry 或 AI Gateway 用的令牌,勾上**「允许上传日志与实验」**。点「新建令牌」。 - 点「复制令牌」。令牌只显示这一次;平台只保存它的哈希。
页面上还会给出一条带着你们项目编号的 curl 命令;能上传的令牌还给出 OTLP 和 Gateway 的地址。列表里显示每个令牌的名称、能做什么、建的人、新建时间和上次使用时间。要撤销,点旁边的垃圾桶。只有在浏览器里登录的人能新建或撤销令牌。v2 之前建的令牌能做的不变;要上传,新建一个。
请求怎么认证
- 令牌:
Authorization: Bearer <令牌>。OTLP 和 Gateway 也收x-api-key: <令牌>,Anthropic 的 SDK 就是这样发的。 - 浏览器:登录的 cookie。浏览器里会改东西的请求必须来自平台自己的页面(
Sec-Fetch-Site: same-origin,或Origin是平台);从别的网站来的回答403 cross_site。CI、脚本和服务器发的请求不带这两个请求头,不受影响。 - 项目令牌只能用在它自己的项目上:别的项目的接口回答
403 project_token_not_allowed。没有上传权限的令牌去上传,回答403 token_scope_missing。别的组织的对象、已删除的项目,回答404 not_found,从不是403。
从 CI 发起运行
POST /v1/projects/<项目编号>/jobs
curl -X POST https://<平台地址>/v1/projects/prj_01J8ZK3Q9MXYZ0000000000000/jobs \
-H "Authorization: Bearer $REVIEW_PROJECT_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT" \
-d '{"commit": "4f2c9e1b7a0d3c5e8f6a1b2c3d4e5f6a7b8c9d0e"}'它和「测试新版本」→「开始测试」一样,发起一轮全部已确认的测试,这一轮变成一个实验。先预跑,预跑通过后再正式运行(对话 AI 的测试集直接发给它;场景每个只跑 1 次、只有正常情况时,运行本身先当预跑)。字段都可以不填:
| 字段 | 类型 | 作用 |
|---|---|---|
commit | 完整的 40 位提交号 | 连 GitHub 仓库的项目:测这个提交,而不是绑定分支的最新提交。 |
image_ref | 镜像地址 | 自带镜像的项目:测这个镜像,只为这次运行读取,并按摘要固定。 |
scope | all、functional、security 或 failed | 跑哪些已确认的测试:全部(默认)、功能测试、安全测试,或上次没通过的。 |
repeats | 1 到 5 | 每个测试跑几次。默认按项目通过标准的要求。 |
notify | true 或 false | 运行结束时给令牌的主人发邮件。默认 true。 |
Idempotency-Key(8 到 128 个字符)可以不带:带同一个键重试,拿回的是第一次的发起,而不是再发起一次。
新发起返回 201,键重复时返回 200:
{
"created": true,
"job": {
"id": "job_…",
"project_id": "prj_…",
"status": "waiting_trial",
"trigger": "api",
"commit_sha": "4f2c9e1b…",
"batch_id": "…",
"run_id": null,
"report_url": null
},
"experiment_id": "exp_…",
"gate_url": "https://<平台地址>/v2/experiments/exp_…/gate"
}experiment_id 是这一轮会变成的实验,发起时就定了(这一轮的第一个运行进来后,实验页在 /p/<项目编号>/experiments/<实验编号>);gate_url 是它的发布门槛,用来等结果(见等发布门槛)。
接的是测试机的项目,要测试机在线。在网页上直连对话 AI 的项目,会把全部已确认的测试集发给那个对话 AI。给不是连仓库的项目带 commit,或给不是自带镜像的项目带 image_ref,都会被拒。
读任务和报告
| 接口 | 返回 |
|---|---|
GET /v1/jobs/<任务编号> | 任务:status、error_code 和 error,运行上传后的 run_id 和 report_url,trial(它的预跑)和 batch(和它一起发起的任务)。沙盒里的任务(和它的 trial)还有 log_last_at 和 cancel_by。waived 写平台为什么不收这个任务的钱:auto_fill 是沙盒中途没了之后平台的自动补跑;model_retest 是只重测平台的模型出错没测成的;其余是 null。补跑的任务 fill_of 是它补的那次运行,fill_mode 是 undecided 时它只重测那次没测成的(见没测成的测试)。 |
GET /v1/runs/<运行编号> | 运行:gate(结论)、trust、status、finding_count、blocker_count、key_missing、各个时间和 report_url。 |
GET /v1/projects/<项目编号>/runs | 项目的运行,最新的在前,每一项和上面一样。?limit= 最多 200(默认 50);?pack=<测试编号> 只看一个测试的运行。 |
任务的 status 从 waiting_trial(等它的预跑)或 waiting(排在一起发起的任务后面)到 queued、claimed、running,最后是 succeeded、failed、cancelled 或 lost。从 API 发起的运行,预跑没通过时,运行以 failed 结束,error_code 是 trial_failed,error 里写原因,这样下一次调用能照常发起。和预跑跑的一样、不另外预跑的运行,从 queued 开始,self_check 是 true,它自己的预跑结论在 trial_result;测试没进到 Agent 时,它回到 waiting_trial(或者照上面说的以 trial_failed 结束),trial 就是它自己跑的那次,self_check 也是 true。沙盒准备环境卡住时,任务以 failed 结束,error_code 是 prepare_stalled(一步 15 分钟没有输出)或 prepare_timeout(快到单次运行的时间上限还没准备好),error 写卡在哪一步和它最后打印的一行,见准备环境卡住。
沙盒里的任务,log_last_at 是沙盒最后一次送来日志的时间,还没有日志时是 null。cancel_by 是请求取消以后的截止时间:到那时沙盒还没停下,平台就删掉它;没请求取消时是 null。测试机和平台执行器上的任务没有这两个字段。
运行的 gate 有四种取值。passed_in_scope 和 passed_reply_level 在页面上都是「通过」,后者只检查了回复。blocked 是有测试没通过,页面上是「不通过」。incomplete 是还没有完整结论:有测试判断不了或没执行、接入看到的不够,或者这次没选通过标准要的关键任务;页面上,接入看到的不够时写「不通过(…)」,其余写「没测成」。key_missing 是通过标准里这次一个结果都没有的关键任务的个数,也就是这次没选的。trust 是 untrusted 表示测试根本没进到 Agent(「没跑起来」),不管结论写的是什么。项目页版本对比用的 GET /v1/projects/<项目编号>/compare 里,每次运行的 verdict 是 passed、failed 或 incomplete(没测成),和报告第一屏一样。
完整的报告 JSON(见下)里,判断不了的每次执行在 verdict.open 里写着具体原因(比如评审模型调用失败、两个评审模型判得不一样、攻击针对的工具在执行前就报错),平台自动重跑过的执行有 verdict.rerun,整次运行重跑了哪些在 reruns 里;是平台自己的问题引起的,两处都多一个 platform(model_relay 是平台模型出错,fixture 是平台自带的测试后端出错),reruns 里还有两次执行的起止时间 spans。第二个评审模型复核了几次、定下几处在 verdict.second_judge,两个判得不一样、按强弱定下的在 verdict.second_judge.split:每处有两边的读法 readings、按谁算 by,why 是 stronger(按更强的),或者推翻更强那个的平台核对(number、denial)。第二个评审模型的用量在 usage.judge.second。测试机同时判几次执行时,usage.judge.concurrency 写最多同时几个评审调用(n),同时有 1、2……个调用时各发出了几个(let_out),以及每段一次只判一件的时候(one_at_a_time:from、to,到运行结束都是一次一件的为 null,why 是 rate_limit 限流、unavailable 暂时不可用、timeout 超时或 near_cap 快到费用上限,status 是当时的状态码)。near_cap 那一段同时发出任务的费用上限放得出的那几个调用:在等的调用按最多花费算还在上限以内,就再放一个(2026 年 11 月 5 日起新放的这个自己不算)。at_most 写几个,在等的那一个按最多花费算就已经放不下时写 1(较早的报告这时不写);放得下更多时这一段结束。见报告。
同时有场景和测试集的一轮会分成几个任务:场景一个,每个测试集各一个,各有自己的运行。要跟着 batch 里的每个任务看。这一轮的报告在 https://<平台地址>/round/<batch_id>。
等发布门槛
GET /v2/experiments/<实验编号>/gate?wait=<秒> 是长轮询:平台那边把请求挂最多 wait 秒(最多 60),有变化、门槛定了或到时就回答,所以 CI 只要在 final 是 true 之前再问,不用每几秒问一次。答复是门槛本身(status 是 pass、blocked 或 none,每条规则在 rules),外加 experiment_status(这一轮第一个运行进来前是 pending,之后是 running、done、cancelled、failed 或 invalid)、final、conclusion(success、failure 或 neutral,和 GitHub check 的结论一样)和 report(每个评分器的平均和对基线的变化、问题标题、实验页)。项目令牌可以读。
GATE=$(curl -fsS -X POST … | jq -r .gate_url)
for i in $(seq 60); do
G=$(curl -fsS -H "Authorization: Bearer $REVIEW_PROJECT_TOKEN" "$GATE?wait=60")
[ "$(echo "$G" | jq -r .final)" = true ] && break
done
case $(echo "$G" | jq -r 'if .final then .conclusion else "neutral" end') in success) exit 0;; failure) exit 1;; *) exit 2;; esacreviewctl eval --launch --project prj_… 和 GitHub Action 的 launch: true 就是这样做的,还会打出门槛的表,退出码 0 通过、1 未通过、2 未判定;见 CI 与 GitHub Action。回答的字段见 v2 接口。
只等任务本身,用 jq 简单地等结果:
JOB=$(curl -fsS -X POST … | jq -r .job.id)
while :; do
J=$(curl -fsS -H "Authorization: Bearer $REVIEW_PROJECT_TOKEN" https://<平台地址>/v1/jobs/$JOB)
case $(echo "$J" | jq -r .status) in succeeded|failed|cancelled|lost) break;; esac
sleep 30
done
RUN=$(echo "$J" | jq -r .run_id)
curl -fsS -H "Authorization: Bearer $REVIEW_PROJECT_TOKEN" https://<平台地址>/v1/runs/$RUN | jq -r .gate用 CLI 令牌还可以读两个接口。GET /v1/runs/<运行编号>/report 返回完整的报告 JSON,加 ?format=markdown 就是 Markdown;返回的是各页面读的那份:复判改过的、AI 诊断按平台原因改成没测成的、有人确认算通过的都已写进去。加 ?fills=1 返回本轮报告读的那份:补跑和「只重测没测成的」合进来、按最后一次算、按通过标准重新下结论,folded 写合进了多少次。GET /v1/runs/<运行编号>/bundle 返回测试机上传的整个运行包(.tar.gz),过了保留期回 410,代码是 evidence_expired。
测试接入和项目设置
这些是网页自己用的接口,列在这里给写脚本时参考:
| 接口 | 做什么 |
|---|---|
PATCH /v1/projects/<项目编号> | 收 auto_rerun(true 或 false):开关平台自动重跑,默认开。项目详情里也有它。 |
GET /v1/projects/<项目编号>/overlay | 平台覆盖层里的测试接入文件,每个带 written_by:person(你们写的)、dev(你们的开发机 AI 写的)或 platform(平台写的);这个字段加上之前存的、审计日志里也查不出来的是 null。 |
PUT /v1/projects/<项目编号>/overlay/<路径> | 存一个测试接入文件。文件读得通、但有跑起来和写的意思不一样的地方时,照样存,回答里多一个 warnings(每条 zh、en 各一句)。 |
GET /v1/projects/<项目编号>/wiring-warnings | 同样的提醒,按沙盒现在读的 review.yaml(覆盖层的,没有就是仓库的):{ "warnings": [...] }。 |
重跑没测成的
重跑实验里未判定的行用的接口(见未判定的行)。GET 用 CLI 令牌也能读。
| 接口 | 做什么 |
|---|---|
GET /v1/runs/<运行编号>/fill | 补跑会跑什么:left、planned、parts、之前的补跑 fills(每个带 fill_mode),做不了时 refused 写原因。?undecided=1:「只重测没测成的」会跑什么,free 写是不是平台的问题(不收费),tests 写每个测试用第几版(was:平台在那次测试后改好、那次测试用的版本)。 |
POST /v1/projects/<项目编号>/jobs | { "kind": "fill", "run_id": "…", "undecided": true, "tests": [{ "scenario": "…", "condition": "…" }] }:只重测这次运行没测成的,tests 不写就是全部;不带 undecided 是补跑没执行的。要带 Idempotency-Key。已经没有没测成的回 422 fill_nothing_undecided。 |
开发机 AI 的接口
开发机上的编码 AI 用 dl_ 令牌调 /v1/dev/*,只在建这个连接的项目里,以建它的人的身份。reviewctl dev 的命令、reviewctl dev mcp 的工具和平台上的 MCP over HTTP 都走这些接口。怎么用见从编码 AI 用平台。
建连接、改权限只能由人在浏览器里做:
| 接口 | 做什么 |
|---|---|
POST /v1/projects/<项目编号>/dev-links | { "allow": {...}, "http": true, "replaces": "<编号>" },都可以不写。不带 http:回一个配对码 code 和连接命令 connect(10 分钟内有效)。带 "http": true:连接马上建好,回 http.url、http.token(dl_ 令牌,只在这次回答里出现)和注册命令 http.claude、http.cursor。replaces:这个项目里一个还在等着配对的码的编号,新的这个代替它(页面上的「换一个配对码」):新码生成时它就作废,回答里的 replaced 写着作废的是哪个;已经连上的连接不会这样被作废。 |
GET /v1/projects/<项目编号>/dev-links | 还在用的连接和等着配对的码:每个的权限 allow、今天用了多少 spent_today_usd、最近做了什么。 |
PATCH /v1/dev-links/<编号> | { "allow": {...} }:改权限,从它的下一次调用起生效,记进审计日志。 |
POST /v1/dev-links/<编号>/resume | 让暂停的连接继续。 |
DELETE /v1/dev-links/<编号> | 断开。 |
allow 是 { "draft", "wiring", "prerun", "launch", "confirm", "assist", "gate", "daily_usd" }:前七个是 true 或 false,依次是「写测试草稿和上传文件」「改测试接入文件」「预跑」「发起正式测试」「确认测试」「让平台的 AI 补全接入」「改通过标准」,daily_usd 是每天最多花多少美元(0 到 1000),null 是不限(只受组织自己的额度和上限约束)。不写 allow 时用网页对话框的默认:除了 confirm 和 gate 都开,daily_usd 是 5,连 GitHub 仓库或自带镜像的项目是 null。这次更新之前建的连接只有 draft 和 prerun。
带 dl_ 令牌的接口:
| 接口 | 对应的工具 | 要的权限 |
|---|---|---|
GET /v1/dev/status | get_status | |
GET /v1/dev/prompt | get_prompt | |
POST /v1/dev/answers | submit_answer | draft |
POST /v1/dev/files | upload_file | draft |
GET /v1/dev/tasks、POST /v1/dev/tasks/<编号>/finish | get_tasks、finish_task | |
GET /v1/dev/wiring、PUT /v1/dev/wiring | get_wiring、put_wiring_file:每个文件带 written_by;连接存的记成 dev,有提醒时回答里带 warnings | 存要 wiring |
GET /v1/dev/overlay | reviewctl dev pull:测试接入文件打成一个 .tar.gz | |
POST /v1/dev/connect-assist、GET /v1/dev/connect-assist | start_connect_assist、get_connect_assist | 发起要 assist |
GET /v1/dev/tests、GET /v1/dev/tests/<id> | list_tests、get_test | |
POST /v1/dev/tests/review | review_drafts | draft |
POST /v1/dev/tests/<id>/confirm | confirm_test | confirm |
POST /v1/dev/trials、GET /v1/dev/trial | try_run、get_setup | 发起要 prerun |
POST /v1/dev/jobs | run_tests | launch,受 daily_usd 约束(null 时受组织剩下的额度约束) |
GET /v1/dev/jobs/<编号>、POST /v1/dev/jobs/<编号>/cancel | get_job / wait_job、cancel_job | 只能取消这个连接发起的 |
GET /v1/dev/runs、GET /v1/dev/runs/<编号>/report、GET /v1/dev/runs/<编号>/diagnosis | list_runs、get_report、get_diagnosis | |
GET /v1/dev/fixes | get_fixes | |
PUT /v1/dev/gate | set_gate | gate |
POST /v1/dev/disconnect | reviewctl dev disconnect | |
GET /v1/dev/plan | get_plan:以前批准过的测试计划和下一步 | |
POST /v1/dev/plan、PUT /v1/dev/plan/answers、POST /v1/dev/plan/next-round、POST /v1/dev/plan/write、PUT /v1/dev/plan | start_plan、answer_plan、next_plan_round、write_plan、edit_plan:已去掉,回答 410,错误码 plan_removed,说明写着改怎么做 | |
GET /v1/dev/v2/experiments、GET /v1/dev/v2/experiments/<编号>、GET /v1/dev/v2/experiments/<编号>/rows | list_experiments、get_experiment、get_experiment_rows | |
GET /v1/dev/v2/traces、GET /v1/dev/v2/traces/<编号>、GET /v1/dev/v2/datasets、GET /v1/dev/v2/gate | list_traces、get_trace、list_datasets、get_gate | |
POST /v1/dev/v2/datasets/<编号>/rows、POST /v1/dev/v2/scorers | add_rows、create_scorer_draft | draft |
POST /v1/dev/mcp | MCP over HTTP:Streamable HTTP、不留会话、直接回 JSON;同一套工具,少了 upload_file。只收 POST。 | 按每个工具 |
测试计划这一步已经去掉(见测试计划):GET /v1/dev/plan 只读以前批准过的计划(plan、in_force)和下一步(next),不再有 approve_url。
测试、任务和运行按编号找时,只在这个连接的项目里找。常见的错误:403 dev_not_allowed(这个连接没有这一步的权限,message 说缺哪个、请人在哪里打开)、403 not_this_connection(任务不是这个连接发起的)、402 dev_daily_cap(这次正式测试最多花的钱超过了今天剩下的额度)、423 dev_paused 和 423 trial_paused(连续 5 次没交成或预跑没通过,等人点「让它继续」)、422 estimate_unavailable(算不出这次正式测试最多花多少)、422 trial_run(get_fixes 或 get_diagnosis 给的是一次预跑的运行,预跑两样都没有:它的问题在 get_setup 里)、409 run_not_ready(任务给出了运行编号,报告还没出来:message 说等哪个任务)。给 dl_ 令牌的每个返回都在响应头 x-reviewctl-locale 里带着这个连接的语言。
账单和额度
组织里的人用 CLI 令牌(或在浏览器里)都能读组织的账单。<组织编号> 是你们组织的(GET /v1/me 里有);别的组织的回 404。
| 路由 | 返回什么 |
|---|---|
GET /v1/orgs/<组织编号>/billing | plan(monthly_cents、included_usage_cents、max_concurrent_sandboxes、max_run_minutes、assistant_daily_usd)和 subscription、本期用量对照套餐包含的量(usage_this_period:usage_cents、included_usage_cents)、wallet(免费版、Pro、Ultra 和先充值有:included_cents 和 included_left_cents 是这一期的额度和还剩多少,balance_cents、credits_cents、used_cents,left_cents 现在还能花多少;企业版是 null)、can_redeem、top_up(min_usd、max_usd、presets;不能用银行卡充值的地方是 null)、can_checkout、can_change_plan、plans(能用银行卡订阅的)、catalog(对比的几个套餐)、花费上限(spend_cap.cap_cents)和 invoices(total_minor 按发票自己的 currency:usd 是美分,以前的 gbp 是便士)。 |
GET /v1/orgs/<组织编号>/usage?month=YYYY-MM | 用量页:totals(按类型的用量和美分)、by_project、by_day、allowance(本月沙盒小时)和 not_charged。not_charged 是平台因为自己的问题多跑、没收费的:cents 是按价格本该收的,by_reason 每项有 reason(auto_fill 自动补跑、rerun 平台自动重跑、sandbox_lost 沙盒中途没了后没留下结果的那段、model_retest 只重测平台的模型出错没测成的、judge_fallback 主评审被限流时改用第二个评审、超出主评审价格的那部分、platform_trial 只因平台自己的部分出错没过的预跑或检查的沙盒时间)、cents 和 sandbox_seconds。这些钱不在 total_cents 里;沙盒时间在 totals.sandbox_seconds 里,不在 allowance.used_hours 里。不带 month 是本月。见平台的问题不收费。 |
GET /v1/orgs/<组织编号>/billing/overview?month=YYYY-MM | 账单看板:wallet、period(本期起止和花费)、usage(和上一行的用量页一样,含 not_charged;不带 month 是本月)、limits(included、spend_cap、sandbox_hours、assistant_today、autonomy,不适用的是 null)、credits(记进余额的每一笔,新的在前,source 是 stripe(银行卡充值)、code 或 admin)和 draws(每一期从余额扣了多少)。金额都是美分;assistant_today 的当天上限既有按模型账单的(cap_usd、used_usd、left_usd),也有按实际收费的(cap_cents 等)。 |
POST /v1/orgs/<组织编号>/credit-codes/redeem | { "code": "PW-…" }:只能由所有者在浏览器里兑换(CLI 令牌和项目令牌会被拒)。回 201,带 cents 和兑换后的 wallet;这个组织已经兑换过这个码时回 200,"duplicate": true。 |
所有金额都以美元计:以 _cents 结尾的字段是美分。所有上限(daily_usd、cap_usd、daily_cap_usd、agent_cap_usd、platform_judge_cap_usd、预估里的 judge_cap_usd 这些)都按实际收的钱写,设的时候也一样,和页面上的数相同(2026 年 10 月 26 日起;以前有几个按模型账单);用量里的 judge_usd、assistant_usd、target_usd 这类是模型账单的美元,收的钱看旁边的 _cents。2026 年 10 月以前这些字段是便士(*_pence),发票的合计是 total_pence。
额度码被拒时回 422,代码是 credit_code_invalid、credit_code_expired、credit_code_revoked 或 credit_code_used。套餐按发票计费(企业版)时回 409 credit_code_plan。一个组织一小时内试了 10 次,或同一个 IP 试了 20 次,之后回 429 credit_code_attempts。见兑换额度码。
付款只能由所有者在浏览器里做(CLI 令牌和项目令牌回 403 session_required)。每个路由都回 { "url": "…" },是要打开的 Stripe 页面:
| 路由 | 做什么 |
|---|---|
POST /v1/billing/checkout | { "plan_id": "pro" }(或 "ultra"):订阅用的 Stripe 付款页。已经有在扣费的订阅时回 409 already_subscribed(改用换套餐),手工管理的套餐回 409 managed_by_hand,422 plan_not_available。 |
POST /v1/billing/top-up | { "amount_usd": 25 },5 到 1000 的整数美元:一次性付款的 Stripe 付款页。Stripe 确认收款后余额才增加,一般回到账单页几秒钟内。400 top_up_amount、409 top_up_plan(按发票计费的套餐)、503 top_up_off(银行卡充值没开通)。 |
POST /v1/billing/portal | {} 打开 Stripe 管理银行卡、发票和取消订阅的页面;{ "flow": "change_plan" } 直接打开 Pro 和 Ultra 之间换套餐的页面。组织还没付过款时回 409 no_customer,没有在扣费的订阅却要 change_plan 时回 409 no_subscription。 |
这三个路由都一样:成员调用回 403 owner_required,网页付款没开通回 503 billing_off,Stripe 没回应回 502 stripe_error。见账单与用量。
告警的 webhook
在「自动化 → 告警」里给告警加一个 webhook,平台在它开始触发、恢复和「试发」时各 POST 一次 JSON 到这个地址(只在 2xx 时算发到,不跟随重定向,10 秒超时)。保存时给出的签名密钥只显示一次。
- 请求头
x-preworld-event:alert.firing、alert.resolved或alert.test。 - 请求头
x-preworld-signature:t=<秒级时间戳>,v1=<十六进制>,v1是用签名密钥对<t>.<请求正文原文>做的 HMAC-SHA256。先比对签名,再看t离现在不超过 5 分钟。 - 正文:
{ "type", "alert": { "id", "name", "project_id", "project", "condition" }, "state", "reading": { "value", "breached", "words": { "zh", "en" } }, "at", "url" }。
import { createHmac, timingSafeEqual } from 'node:crypto';
const [t, v1] = header.split(',').map((p) => p.split('=')[1]);
const want = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
const ok = timingSafeEqual(want, Buffer.from(v1, 'hex')) && Math.abs(Date.now() / 1000 - Number(t)) <= 300;错误
出错时返回一个 HTTP 状态码和一个 JSON 对象:
{ "error": { "code": "project_busy", "message": "这个项目已有排队或执行中的任务;等它结束或取消后再发起" } }code 不随语言变;message 是给人看的。带 x-reviewctl-locale: zh 或 en 请求头选择提示的语言。不带的话,项目 API 令牌按令牌主人的语言回答,没有就按组织的;其他请求用中文回答。有的错误还带 detail,是解析器自己的原话。请求体格式不对是 400 invalid_body,校验器的提示是英文。
| 状态码 | 常见的码 | 怎么办 |
|---|---|---|
| 400 | invalid_body、idempotency_key_invalid、bad_filter(带 position)、bad_cursor、invalid_characters | 改请求。查询参数超出范围(比如 limit=1000)也是 invalid_body。 |
| 401 | unauthorized | 令牌写错了、被撤销了,或者建它的人离开了:新建一个。 |
| 402 | trial_ended、trial_credit_used、subscription_inactive、prepaid_used、sandbox_quota、spend_cap_reached | 套餐现在不让发起新的运行或花钱,见测试发不起来的时候。 |
| 403 | project_token_not_allowed、token_scope_missing、session_required、cross_site、org_suspended、terms_required | 项目令牌调了它不能调的接口或没有上传权限;这一步只能由人在浏览器里做;浏览器请求来自别的网站;组织被暂停了;或者用的是 CLI 令牌,令牌的主人要先在浏览器里同意条款。 |
| 404 | not_found、version_not_found、row_not_found、baseline_not_found | 不在你们组织里,或者编号写错了。 |
| 409 | project_busy、idempotency_conflict、lost_job_unacknowledged、edit_in_scenario_editor、experiment_finished、derived_experiment、idempotency_key_reused | 已经有运行在等或在跑;同一个键用在了不同的发起或批次上;有运行在执行中失联,要有人在网页上确认已经核对过测试后端(后端都在沙盒里时由平台自己核对,见失联以后);场景的行要在场景编辑页改;已经结束的实验、或从运行来的实验不能再加行。 |
| 422 | nothing_to_test、commit_not_for_project、image_ref_not_for_project、no_runner、no_chat_ai、chat_ai_ambiguous、unknown_scorer、unknown_dataset、unknown_baseline、invalid_scorer | 项目没法按要求运行,或者请求里写了项目没有的东西,提示里写了原因。 |
| 429 | rate_limited、assistant_allowance | 登录尝试或 Gateway 调用太多(等给出的秒数),或者助手的每日额度用完了。 |
| 5xx | internal | 稍后再试;一直这样的话,看服务状态。 |
频率限制
用项目 API 令牌调 /v1、/v2 和 OTLP 没有次数配额。限制运行的是项目和组织:
- 一个项目同时只能有一次发起:有运行在等或在跑时,再发起会返回
409 project_busy。 - 超出组织上限的沙盒排队等候(见上限)。
- AI Gateway 默认每个组织每分钟 600 次,超过返回
429 rate_limited,带Retry-After(Gateway 的上限)。 - 登录相关的接口(登录链接、邀请、用 GitHub 和 Google 登录、命令行设备码、测试机配对)每个 IP 每分钟 20 次,超过返回
429 rate_limited。
下一步
- v2 接口:数据集、评分器、实验、门槛和追踪。
- CI 与 GitHub Action:在 CI 里等发布门槛。
- 命令行:在 CI 里跑测试集用
reviewctl eval。 - 账单与用量:运行怎么收费,发起为什么被拒。
- 常见问题:现象和修法。