Campaign API 让程序读取活动Developer 组织的一组数据工作及其相关设置定义、检查候选更改、创建活动或把更改应用到草稿,并执行一次已由人确认的发布。它与订阅数据交付是两个契约:交付记录从 HTTP 数据接口读取,数据读取 Key 没有下面任何一项权限。
地址、凭证与权限
正式环境使用 https://api.humanbased.ai 和该环境签发的 Key,测试环境使用 https://api.staging.humanbased.ai 和测试环境的 Key。所有操作都在 /api/v1/campaignapi/ 下,请求头为 Authorization: Bearer <API_KEY>。
Key 由组织成员在门户创建,创建时选择活动权限,表单见创建和使用 API Key。服务端对每项权限单独检查。
| 权限 | 这把 Key 可以 | 范围 |
|---|---|---|
campaign:read | 列出并读取活动、版本和模板。 | 只读 Key 可读本组织的所有活动。 |
campaign:author | 验证和规划候选文件、创建活动、把更改应用到草稿、读取应用回执。 | Key 的 managed_campaign_ids 列出的活动:创建 Key 时创建者可以编辑的那些,加上这把 Key 自己创建的活动。 |
campaign:launch | 提出发布提案、读回自己的提案,并在人确认后执行。 | 同一份名单,且在 Key 记录的支出上限之内。 |
活动在 Key 的组织或名单之外时,返回 404 和 CAMPAIGN_NOT_FOUND,与活动根本不存在时的回答相同。
创建 Key 的成员要在本组织至少一个活动上是编辑者或所有者,表单才会签发 campaign:author 和 campaign:launch。组织的第一个活动请在 Developer 门户创建。
确认身份和可读范围
先执行只读身份检查:
curl --silent --show-error \
'https://api.humanbased.ai/api/v1/auth/whoami' \
--header "Authorization: Bearer $HB_API_KEY"继续之前,核对 data.org_id、data.scopes 和 data.managed_campaign_ids。Key 放在环境变量或密钥管理工具中。然后读取当前身份可见的活动:
curl --silent --show-error \
'https://api.humanbased.ai/api/v1/campaignapi/campaigns' \
--header "Authorization: Bearer $HB_API_KEY"每个响应都带 success、errorCode、errorMessage 和 data。活动列表可传 status、limit(1–200)和不透明的 cursor。读取 data.items;当 data.has_more 为 true 时,原样把 data.next_cursor 传入下一次请求。单页为空时继续查看 has_more 和 next_cursor,再判断分页是否结束;标识符按字符串处理。
按目的选择接口
| 目的 | 操作 | 所需权限 |
|---|---|---|
| 查找 Campaign 文件可以引用的模板 | GET /api/v1/campaignapi/templates、template、catalog | campaign:read |
| 查看活动和已保存的版本 | GET /api/v1/campaignapi/campaigns、campaign、versions、version、versiondiff | campaign:read |
| 只检查候选文件 | POST /api/v1/campaignapi/candidatevalidate、candidateplan | campaign:author |
| 创建活动,或把更改应用到草稿 | POST /api/v1/campaignapi/candidateapply | campaign:author |
| 读取此前一次应用的回执 | GET /api/v1/campaignapi/actionreceipt | campaign:author |
| 提出发布提案,并读回提案 | POST /api/v1/campaignapi/launchpropose、GET /api/v1/campaignapi/launchproposal | campaign:launch |
| 确认发布提案 | POST /api/v1/campaignapi/launchconfirm | 登录门户的人;Key 会被拒绝 |
| 执行已确认的发布 | POST /api/v1/campaignapi/launch | campaign:launch |
标识符在 GET 请求中放查询参数,在 POST 请求中放 JSON 请求体。CLI 把同样的操作封装成命令。
编写 Campaign 文件
候选文件是一份 Campaign 文件:一个 JSON 对象,装着整个活动的定义。每次写入都发送完整文件。
| 部分 | 内容 |
|---|---|
spec_version | 字符串 "1.0"。 |
campaign | 活动的 name。campaign_id、owner 和 created_at 由服务端填写。 |
eligibility | 谁可以参与:gates、kyc_tier(none、light 或 full)和 reputation。 |
workflow | 各个步骤 nodes,以及步骤之间的先后 next。 |
reward_policy | 基础奖励 base、调整项 factors 和上限 caps。 |
quotas | 活动的总预算 budget_usd,以及各步骤的限额 per_node。 |
data_contract | scarcity_strata 和 lineage。 |
audit | 一句话的 intent,以及 versions。 |
八个部分都必填。平台没有为这份文件单独发布 JSON Schema,candidatevalidate 的检查结果就是准绳,每个问题都带字段路径。
修改已有活动时,读回它的最新版本再编辑,做法见下一节。新建时可以从下面这份文件开始:一个参与者步骤,前面有准入检查,每条提交由人验收。名称、金额和数量只是示意,请填你自己的决定。template_id 从 GET /api/v1/campaignapi/templates 取得。
{
"spec_version": "1.0",
"campaign": { "name": "退货分诊第一轮" },
"eligibility": { "gates": [], "kyc_tier": "none", "reputation": {} },
"workflow": {
"nodes": [
{ "key": "gate", "node_key": "access_gate", "params": { "spec_version": 1 } },
{
"key": "collect",
"node_key": "human_supply",
"params": {
"form_template_ref": { "template_id": "784210002001" },
"task": { "totalCap": 200, "perUserQuota": 20 },
"reward": { "asset_code": "USDC", "amount": 0.5 }
}
},
{ "key": "accept", "node_key": "data_accept", "params": { "acceptance": { "mode": "manual" } } }
],
"next": [
{ "from": "gate", "to": "collect" },
{ "from": "collect", "to": "accept" },
{ "from": "accept", "to": "done" }
]
},
"reward_policy": {
"base": { "asset_code": "USDC", "amount": 0.5 },
"factors": [],
"caps": {}
},
"quotas": { "per_node": {}, "budget_usd": 100 },
"data_contract": { "scarcity_strata": [], "lineage": {} },
"audit": {
"intent": "收集退货案例的分诊结论。",
"versions": []
}
}在 collect 步骤里,task.totalCap 是这一步向全体参与者收取的提交总数,task.perUserQuota 是一位参与者最多可以提交的条数。两者都把进行中的任务和等待验收的提交计算在内。提交被驳回或判为重复后不再计入,名额随之空出。reward.amount 是一条已接受提交的奖励。
在 accept 步骤里,acceptance.mode 设为 manual 后,每条提交都要等人验收。不写这一项,提交一到就自动接受。
hb campaign init --template 写出的文件有同样的部分和步骤,包括 task 和 acceptance;需要你决定的每个值都留成 <DECIDE: …> 占位符。
验证并规划候选文件
这两个操作只读取。把上面的文件存为私有工作目录里的 candidate.json,再生成新建活动所需的两个请求体:
python3 - <<'PY'
import json
from pathlib import Path
candidate = json.loads(Path('candidate.json').read_text())
Path('validate.json').write_text(json.dumps({'candidate': candidate}))
Path('plan.json').write_text(json.dumps({'mode': 'create', 'candidate': candidate}))
PY要规划一次修改,就把 HB_CAMPAIGN_ID 设为这把 Key 可以编辑的活动,读取它的最新版本:
curl --fail-with-body --silent --show-error --get \
'https://api.humanbased.ai/api/v1/campaignapi/version' \
--header "Authorization: Bearer $HB_API_KEY" \
--data-urlencode "campaign_id=$HB_CAMPAIGN_ID" \
--output version.jsondata.version.version_number 与 data.version.spec_hash 确定本次修改的原版本。data.campaign_file 是完整文件,data.server_assigned_fields 列出 campaign 下候选文件要去掉的字段。下面只示范修改名称:
python3 - <<'PY'
import json
from pathlib import Path
response = json.loads(Path('version.json').read_text())
assert response['success'] and response['data']['campaign_file']
data = response['data']
candidate = data['campaign_file']
for field in data.get('server_assigned_fields', []):
candidate['campaign'].pop(field, None)
candidate['campaign']['name'] = '已复核的示例名称'
Path('validate.json').write_text(json.dumps({'candidate': candidate}))
Path('plan.json').write_text(json.dumps({
'mode': 'update', 'campaign_id': data['campaign_id'],
'candidate': candidate,
'expected_version_number': data['version']['version_number'],
'expected_spec_hash': data['version']['spec_hash'],
}))
PY两种情况的请求体用同样的方式发送:
curl --fail-with-body --silent --show-error \
'https://api.humanbased.ai/api/v1/campaignapi/candidatevalidate' \
--header "Authorization: Bearer $HB_API_KEY" \
--header 'Content-Type: application/json' --data-binary @validate.json \
--output validation.json
curl --fail-with-body --silent --show-error \
'https://api.humanbased.ai/api/v1/campaignapi/candidateplan' \
--header "Authorization: Bearer $HB_API_KEY" \
--header 'Content-Type: application/json' --data-binary @plan.json \
--output plan-result.json| 文件 | 查看 | 含义 |
|---|---|---|
validation.json | data.valid | 所有检查都通过时为 true。 |
validation.json | data.checks | schema、compile、dependencies 各一项,带 ran、passed 和 violations。每条问题有 path、稳定的 code 和 message。 |
validation.json | data.candidate_hash | 你发送的这份文件的标识。 |
plan-result.json | data.applicable | 候选文件有效,且修改时原版本仍是草稿,才为 true。 |
plan-result.json | data.base | 计划对照的版本;新建时为 null。 |
plan-result.json | data.changes、data.change_count | 每项更改的 op(add、remove 或 replace)、path,以及 before 或 after。 |
plan-result.json | data.findings | 同一份验证结果,随这次计划再给一遍。 |
applicable: false 的计划还不能应用。原版本或哈希变化后,规划会以 BASE_VERSION_STALE 被拒绝:重新读取版本,再复核新的候选文件和计划。
创建活动
负责人批准这份文件后,在新建计划的请求体里加上 request_key,发送给 candidateapply。请求键由你自己选:一个稳定的字符串,最长 200 个字符,只代表这一次写入。
python3 - <<'PY'
import json
from pathlib import Path
body = json.loads(Path('plan.json').read_text())
assert body['mode'] == 'create'
body['request_key'] = 'create-returns-triage-2026-10-10'
Path('apply.json').write_text(json.dumps(body))
PY
curl --fail-with-body --silent --show-error \
'https://api.humanbased.ai/api/v1/campaignapi/candidateapply' \
--header "Authorization: Bearer $HB_API_KEY" \
--header 'Content-Type: application/json' --data-binary @apply.json \
--output receipt.json新建请求体只有 mode、candidate 和 request_key,其中没有 campaign_id,也没有原版本字段。
一次新建在同一个 request_key 下依次完成这些步骤:
| 步骤 | 结果 |
|---|---|
| 分配 | 在 Key 所属组织建立一个草稿活动,归创建这把 Key 的人所有,预算取自 quotas.budget_usd。 |
| 开通 | 这个活动的 Pipeline,此时只有一个准入检查步骤。发布时,平台才把草稿里的步骤写进去。 |
| 保存 | 把文件存为第 1 版,状态为 draft。 |
| 授权 | 把新活动加入 Key 的 managed_campaign_ids,同一把 Key 之后可以修改并发布它。granted_to_key 说明这一步是否完成。 |
响应是一份回执。示例中的哈希做了缩写:
{
"data": {
"replayed": false,
"action_id": "784210008001",
"action": "campaignfile.apply",
"status": "succeeded",
"request_key": "create-returns-triage-2026-10-10",
"request_hash": "5d0c…a917",
"campaign_id": "784210001001",
"result": {
"campaign_id": "784210001001",
"external_pipeline_id": "784210011001",
"file_version_id": "784210007001",
"version_number": 1,
"persisted_file_hash": "8a41…c2f0",
"state": "draft",
"granted_to_key": true
}
},
"success": true,
"errorCode": 0,
"errorMessage": "SUCCESS"
}保存 data.campaign_id,之后每个操作都要用它。data.result.granted_to_key 为 false 时,活动已经建立,但这把 Key 没有拿到它;请在 Developer 门户修改,或换一把名单中包含它的 Key。要确认创建结果,读取 GET /api/v1/campaignapi/campaign?campaign_id=<campaign_id>,data.campaign.status 应为 draft。
persisted_file_hash 与验证时的 candidate_hash 不同,因为保存的文件多了服务端填写的字段。
把更改应用到草稿
在修改计划的请求体里加上 request_key,发送给同一个操作。修改请求体包含 mode、campaign_id、candidate、expected_version_number、expected_spec_hash 和 request_key。
修改成功后,候选文件成为这个活动最新的草稿版本,其他内容保持原样。回执结构与上例相同:data.result.version_number 是新版本号,data.result 中没有 granted_to_key。
写入的那一刻,服务端会再比对一次版本号和哈希。别人先保存时,请求以 409 和 BASE_VERSION_STALE 被拒绝,什么都没有写入:重新读取版本、重做候选文件,再规划一次。
读取回执并恢复
candidateapply 返回 200 时,一定是 status: succeeded 的回执。其他结果都以错误响应返回;另外三种状态通过下面的回执查询看到。
| 回执字段 | 含义 |
|---|---|
replayed | 为 true 时,这份响应描述的是同一 request_key 下此前的尝试,本次没有新的写入。查询回执时它总是 true,与那次尝试的 status 无关。 |
action_id | 服务端给这次写入的标识。 |
action | 新建和修改都是 campaignfile.apply。 |
status | succeeded、in_progress、failed 或 uncertain。 |
request_key、request_hash | 你的请求键,以及服务端对整个请求计算的哈希。 |
campaign_id、result | 被写入的活动,以及这次写入的产出。 |
steps、uncertain_reason | 写入尚未了结或已失败时出现:进行到哪一步,以及结果为何未知。 |
in_progress_since | status 为 in_progress 时出现:这次写入最近一次有进展的时间。 |
request_key 让重试变得安全。把请求体和它的键保存在一起,再按重试的返回决定:
| 你看到 | 发生了什么 | 你现在做什么 |
|---|---|---|
| 完全没有响应 | 写入可能成功,也可能没有。 | 读取 GET /api/v1/campaignapi/actionreceipt?request_key=<your-key>,或用同一个键重发相同的请求体。 |
回执 replayed: true 且 status: succeeded | 首次尝试已成功,这次没有重复写入。 | 以这份回执为准。 |
409 ACTION_IN_FLIGHT,或回执 status: in_progress | 仍有请求在用这个键写入。 | 稍等后读取回执。in_progress_since 过去十分钟仍是 in_progress 时,用同一个键重发相同的请求体。 |
409 REQUEST_KEY_SPENT,或回执 status: failed | 此前的尝试失败,什么都没有写入。 | 处理原因后换新键再发。 |
409 REQUEST_KEY_REUSED | 这个键已经用于另一个请求。 | 重试原请求就发原请求体;新请求换新键。 |
409 ACTION_UNCERTAIN,或回执 status: uncertain | 写入已经尝试,结果未知。 | 停下。由人对照回执的 steps 和活动当前版本后再决定;新建时,steps 会写明已经分配的活动。换新键等于第二次写入。 |
actionreceipt 只接受 request_key 或 action_id 其中一个。按 request_key 查询时,默认查 Campaign 文件的应用;查模板的应用要加 action=template.apply。发布的回执要按下一节的方式找回。
发布活动
发布把草稿活动变为 live,并从组织余额中锁定它的预算。这件事由三方动作完成:Key 提出提案,人确认这份提案,Key 再执行。
提出提案前先核对四项:
| 前提 | 在哪里决定 |
|---|---|
Key 有 campaign:launch,且活动在它的 managed_campaign_ids 中。 | Key 的授权,用 whoami 查看。 |
| 最新存储的版本有工作进入的步骤,每个参与者步骤都有表单。 | Campaign 文件。 |
| 活动设有预算,组织可用余额足以覆盖。 | 提案的 amount_usd,以及组织的 Billing 页面。 |
| 预算在 Key 支出上限的剩余额度之内。 | 创建 Key 时设定的上限。 |
1. 提出提案
curl --fail-with-body --silent --show-error \
'https://api.humanbased.ai/api/v1/campaignapi/launchpropose' \
--header "Authorization: Bearer $HB_API_KEY" \
--header 'Content-Type: application/json' \
--data "{\"campaign_id\": \"$HB_CAMPAIGN_ID\"}" \
--output proposal.json提案只写入一条记录,资金和活动都保持原样。请保存响应,proposal_id 只在这里返回。
{
"data": {
"proposal_id": "784210010001",
"campaign_id": "784210001001",
"operation": "launch",
"state": "proposed",
"amount_usd": "100.0000",
"base_version_number": 1,
"base_spec_hash": "8a41…c2f0",
"proposal_hash": "clp1_77be…09d3",
"expires_at": "2026-10-10T10:30:00+00:00",
"confirmed_by": null,
"confirmed_at": null,
"executed_action_id": null
},
"success": true,
"errorCode": 0,
"errorMessage": "SUCCESS"
}amount_usd 是这次发布将锁定的预算,以保留四位小数的字符串给出。base_version_number 和 base_spec_hash 指明它将运行的草稿。proposal_hash 把这些内容绑在一起。expires_at 是提出提案后一小时,确认和执行都要在这之前完成。
一把 Key 对一个活动只保留一份未了结的提案。第一份执行完或过期之前,对同一活动再次调用 launchpropose 会以 PROPOSAL_OPEN_ALREADY 被拒绝。
2. 由人确认
把负责这项决定的人引到 Developer 门户的确认页面:
https://developer.humanbased.ai/campaigns/<campaign_id>/launch/confirm?proposal=<proposal_id>对方以该活动的编辑者或所有者身份登录。页面显示服务端保存的活动、金额、草稿版本、过期时间和提案哈希。选择 Confirm this launch(确认这次发布)只记录对这个哈希的同意,发布留到下一步执行。
用提出提案的那把 Key 读取 GET /api/v1/campaignapi/launchproposal?proposal_id=<proposal_id>。data.state 变为 confirmed 后继续。
3. 执行
python3 - <<'PY'
import json
from pathlib import Path
proposal = json.loads(Path('proposal.json').read_text())['data']
Path('launch.json').write_text(json.dumps({
'proposal_id': proposal['proposal_id'],
'request_key': 'launch-returns-triage-2026-10-10',
}))
PY
curl --fail-with-body --silent --show-error \
'https://api.humanbased.ai/api/v1/campaignapi/launch' \
--header "Authorization: Bearer $HB_API_KEY" \
--header 'Content-Type: application/json' --data-binary @launch.json \
--output launch-receipt.json请求体包含 proposal_id 和你选定的 request_key,并且只能由提出提案的那把 Key 发送。资金变动之前,服务端按活动此刻的状态重新计算提案哈希。预算、草稿或付款条款在确认之后改过,就成了另一个动作,请求会以 PROPOSAL_STALE 被拒绝。
发布成功时返回 action: campaign.launch、status: succeeded 的回执。data.result 中有 campaign_id、活动的新 status、proposal_id 和 confirmed_by。
确认结果或找回发布
| 你看到 | 发生了什么 | 你现在做什么 |
|---|---|---|
回执 status: succeeded | 活动已发布,预算已锁定。 | 用 GET /api/v1/campaignapi/campaign 确认 data.campaign.status 为 live。提案的 state 为 executed。 |
| 完全没有响应 | 发布可能已执行,也可能没有。 | 用同一个 request_key 重发相同的请求体。服务端返回首次的回执,发布只发生一次。actionreceipt?request_key= 查不到发布。 |
PROPOSAL_NOT_CONFIRMED | 还没有人确认。 | 请对方确认,再发送同一请求体。 |
PROPOSAL_EXPIRED 或 PROPOSAL_REVOKED | 这份提案已经关闭。 | 重新提出提案,并请人确认新提案。 |
PROPOSAL_STALE | 活动在确认之后发生了变化。 | 等这份提案的 expires_at 过去后重新提出;在那之前,新提案会以 PROPOSAL_OPEN_ALREADY 被拒绝。 |
SPEND_CAP_EXCEEDED | 这次发布会超出 Key 的支出上限,资金没有变动。 | 请人在 Developer 门户发布,或用一把上限有余量的 Key 重新提出提案。 |
CAMPAIGN_GRAPH_MALFORMED | 草稿无法按存储的内容运行:Pipeline 拒绝了其中的步骤,或它引用的表单模板不存在或为空。资金没有变动。 | 提交更正后的草稿,等这份提案的 expires_at 过去后重新提出提案。 |
503 CAMPAIGN_GRAPH_UNAVAILABLE | Pipeline 没有接收这份草稿。资金没有变动,活动也没有变为 live。 | 换一个新的 request_key 再次执行发布。这份提案仍处于已确认状态。 |
PROPOSAL_ALREADY_EXECUTED | 这份提案已经执行,或正在执行。 | 读取提案和活动的状态,保持现状。 |
409 ACTION_UNCERTAIN | 发布已经尝试,结果未知,预算可能已被锁定。 | 停下。读取 launchproposal:executed 表示发布已完成,executing 表示尚未了结。由人核对活动状态和组织的 Billing 页面,再带上 proposal_id 联系 hello@humanbased.ai。换新键等于第二次发布。 |
错误
拒绝响应的 success 为 false,data 为 null,errorCode 等于 HTTP 状态,并带有 errorCodeName。程序按 errorCodeName 分支。errorMessage 通常是给人读的文字,措辞可能调整;CANDIDATE_INVALID 的 errorMessage 则是一个对象,带有校验结果:
{
"data": null,
"success": false,
"errorCode": 400,
"errorCodeName": "CANDIDATE_INVALID",
"errorMessage": {
"error": "CANDIDATE_INVALID",
"message": "the candidate did not pass validation, so nothing was applied",
"findings": {
"valid": false,
"checks": [
{"check": "schema", "ran": true, "passed": false, "violations": [{"path": "…", "code": "…", "message": "…"}]}
],
"candidate_hash": "…"
}
}
}findings 就是 candidatevalidate 在 data 中返回的对象,示例做了删节。打印 errorMessage 前,先判断它是不是字符串。
errorCodeName | 发生了什么 | 你现在做什么 |
|---|---|---|
KEY_CAPABILITY_MISSING | Key 缺少这个操作需要的权限。 | 创建带有该权限的 Key。 |
KEY_HAS_NO_CAMPAIGN_ALLOWLIST | Key 可以读取组织的活动,但名单里没有它可以修改的活动。 | 创建一把带写入权限的替代 Key。 |
KEY_HAS_NO_ACCOUNTABLE_PERSON | Key 没有记录创建它的人,它建立的活动会没有所有者。 | 创建一把替代 Key。 |
CAMPAIGN_NOT_FOUND | 活动不存在,或这把 Key 看不到它。 | 对照 campaigns 列表和 managed_campaign_ids 核对 ID。 |
CANDIDATE_MALFORMED | 候选文件不是 Campaign 文件结构的 JSON 对象。 | 修正文件结构。 |
CANDIDATE_TOO_LARGE | 候选文件超过规模限制:1,048,576 字节、20,000 个 JSON 值、64 层嵌套、256 个工作流节点或 1,024 条连线。 | 精简文件。 |
CANDIDATE_INVALID | 候选文件经过检查但未通过。什么都没有应用,request_key 仍未使用。 | 读取 errorMessage.findings,逐条修正后再发送。 |
EXPECTED_VERSION_REQUIRED、EXPECTED_SPEC_HASH_REQUIRED | 修改请求没有指明原版本。 | 把读到的版本号和哈希都带上。 |
EXPECTED_BASE_ON_CREATE | 新建请求带了 campaign_id 或原版本字段。 | 去掉这些字段。 |
REQUEST_KEY_REQUIRED | 写入请求没有 request_key,或键是空白。 | 选定一个键再发送。 |
CAMPAIGN_HAS_NO_VERSION | 修改请求指向的活动还没有可对照的版本。 | 规划前先读取该活动的版本列表。 |
BASE_VERSION_STALE | 原版本在你读取之后发生了变化。 | 重新读取版本、重做候选文件,再规划一次。 |
CONTENT_IS_HISTORICAL | 这份内容与某个较早的版本完全相同,而当前已有更新的版本。什么都没有改变。 | 发送一份与那个旧版本有差别的文件。 |
PLATFORM_NOT_CONFIGURED | 平台缺少新建所需的配置,什么都没有创建。 | 联系 hello@humanbased.ai;重试没有帮助。 |
DEVELOPER_SESSION_REQUIRED | 用 Key 调用了 launchconfirm。 | 请人在 Developer 门户确认。 |
PROPOSAL_NOT_FOUND | 没有这份提案,或它由另一把 Key 提出。 | 使用提出提案的那把 Key 和它收到的 proposal_id。 |
PROPOSAL_HASH_MISMATCH | 确认时发送的哈希与保存的提案不一致。 | 重新读取提案,按它显示的哈希确认。 |
SPEND_CAP_UNAVAILABLE | Key 可以发布,但它的支出上限没有读到,发布没有执行。 | 再发送一次同样的请求;持续出现时创建替代 Key。 |
ACTION_NOT_FOUND | 这把 Key 名下没有与该 action_id 或 request_key 对应的回执。 | 核对标识,以及它所属的 action。 |
ACTION_LOOKUP_REQUIRED | 查询回执时没有提供,或同时提供了 action_id 和 request_key。 | 只发送其中一个。 |
CAMPAIGN_GRAPH_NO_WORK_ORIGIN | 流程里没有让工作进入的任务或导入节点。 | 添加一个,保存新版本后再发布。 |
CAMPAIGN_ID_REQUIRED | 该操作针对一个活动,但请求没有指明。 | 发送 campaign_id。 |
CAMPAIGN_ROLE_INSUFFICIENT | 你在该活动上的角色低于该操作的要求。 | 请活动所有者授予可执行该操作的角色。 |
CAMPAIGN_VERSION_NOT_FOUND | 该活动没有这个版本。 | 先读取 versions,使用列出的版本。 |
CLI_SESSION_NOT_PERMITTED | 已登录的 CLI 不能执行该操作。 | 改用组织 API Key;重新登录也不会改变结果。 |
CURSOR_INVALID | 这个 cursor 不是该接口签发的。 | 不带 cursor 重新开始,再沿用返回的 cursor。 |
KEY_PRINCIPAL_INCOMPLETE | 网关转发的 Key 身份不完整。这是平台故障,不是 Key 有误。 | 重试;持续出现时联系 hello@humanbased.ai。 |
LIMIT_OUT_OF_RANGE | limit 超出该接口接受的范围。 | 使用范围内的值,例如 campaigns 为 1–200。 |
MODE_UNSUPPORTED | 该操作不提供这种模式。 | 使用该操作列出的模式。 |
OBSERVATION_OWNER_DISAGREES | 该活动指向的流程在其所属服务中不存在。这是配置故障,不是请求有误。 | 带上 Campaign ID 联系支持。 |
OBSERVATION_OWNER_UNAVAILABLE | 负责这些计数的服务没有响应,请求并未被拒绝。 | 重新读取。 |
OBSERVATION_REFUSED_BY_OWNER | 负责这些计数的服务拒绝了请求,没有说明原因。 | 带上 Campaign ID 和时间联系支持。 |
OBSERVATION_WINDOW_INVALID | 统计窗口或流程版本被拒绝;errorMessage 会引用该值。 | 修正该值后重新读取。 |
ORG_ID_REQUIRED | 调用方没有对应的组织,因此没有可读取的范围。 | 使用属于某个组织的 Key 或登录身份。 |
ORG_MISMATCH | 该 CLI 会话登录的组织与请求指定的组织不同。 | 登录到该组织,或去掉请求中显式指定的组织。 |
PIPELINE_VERSION_NOT_FOUND | 该流程版本不属于这个活动的流程。 | 使用该活动的版本。 |
PORTAL_SESSION_NOT_PERMITTED | 通过此 API 的发布绑定到提出它的组织 API Key 或已登录的 CLI,Developer 门户的登录会话两者都不是。 | 在 Developer 门户的活动页面发布,或用同一把 Key 或同一个 CLI 会话提出并执行发布。 |
REQUEST_MALFORMED | 请求无法解析:缺少字段、类型错误或超出范围。 | 阅读 errorMessage,修正字段后重新发送。 |
TEMPLATE_INVALID | 模板经过检查,其中有错误。 | 按每条带路径的结果修正(与 templatevalidate 返回的相同),再重新发送。 |
TEMPLATE_KIND_IMMUTABLE | 模板类型在创建时已固定。 | 改为新建一个模板。 |
TEMPLATE_MALFORMED | 请求体根本不是模板文档。 | 修正请求结构。 |
TEMPLATE_NOT_EDITABLE | 本组织可以读取该模板,但不能修改。 | 先采用到你的组织,再编辑副本。 |
TEMPLATE_NOT_FOUND | 没有该模板,或本组织无权使用。 | 核对模板 ID。 |
TEMPLATE_STALE | 读取之后模板已被修改。 | 重新读取,把你的修改并入最新内容,并带上新的哈希发送。 |
ACTION_IN_FLIGHT、ACTION_UNCERTAIN、REQUEST_KEY_REUSED、REQUEST_KEY_SPENT | 用 request_key 的写入仍在进行、已经失败、结果未知,或这个键已用于另一个请求。 | 按读取回执并恢复中的表处理。遇到 ACTION_UNCERTAIN 时先停下,由人核对后再决定是否重发。 |
PROPOSAL_NOT_CONFIRMED、PROPOSAL_EXPIRED、PROPOSAL_REVOKED、PROPOSAL_STALE、PROPOSAL_OPEN_ALREADY、PROPOSAL_ALREADY_EXECUTED、SPEND_CAP_EXCEEDED、CAMPAIGN_GRAPH_MALFORMED、CAMPAIGN_GRAPH_UNAVAILABLE | 发布提案或执行发布被拒绝。 | 按发布活动中的拒绝原因表处理。 |
请求到达本接口之前被拒绝时,例如 Key 缺失或无效的 401,响应可能没有 errorCodeName,程序要为这种情况保留默认分支。网关返回的 429 Too many requests 也是如此:按调用频率限制等待 Retry-After 给出的秒数。
在 Developer 门户发布的做法见测试与发布。