# 读取交付记录：接口参考


`GET /v1/live/pull` 支持增量交付和从头读取当前可读范围。首次接入请先按[小范围读取指南](/zh/api/api-data)保存和检查一页。

## 环境与认证

| 环境 | API 地址 |
| --- | --- |
| 正式环境 | `https://api.humanbased.ai` |
| 测试环境 | `https://api.staging.humanbased.ai` |

在请求头传入该环境签发的完整组织密钥：`Authorization: Bearer <API_KEY>`。Key 必须有效、未过期、有数据交付权限，且覆盖目标订阅。订阅应属于该 Key 的组织并处于有效状态。网页登录会话、掩码 Key 和 Campaign ID 都不能代替它；不要把 Key 放入 URL 查询参数。

认证通过也不会扩展数据使用条款。下载、分享、保留或训练用途应另外符合适用授权。

## 两种读取模式

| | 从头读取 | 增量交付 |
| --- | --- | --- |
| 请求 | 带 `from=beginning`。 | 不带 `from`。 |
| 用途 | 读取或恢复订阅当前可读范围。 | 取得共享读取位置之后的记录。 |
| 分页 | 用最新 `next_cursor` 作为下一页的 `cursor`，保持筛选条件。 | `has_more=true` 时继续请求；服务端保存进度。 |
| 共享进度 | 不推进。 | 按订阅推进，其他 Key 和程序也受影响。 |
| 计量 | 不产生新的计量拉取费用；`receipt` 为空。 | 新交付记录可能按订阅价格计费。 |
| 筛选 | 可用任务或 Campaign ID 缩小范围。 | 不接受这两类筛选。 |

零价订阅可从头读取符合条件的已交付记录，包括增量尚未取过的记录。收费订阅只能重读此前已取得的范围；从头读取不能绕过计费。该接口不保证冻结的数据快照，新交付或更正可能改变之后可读的内容。

增量请求虽然使用 GET，仍会改变订阅共享的读取位置。每次完整保存响应后再继续。如果响应丢失，位置可能已经前进；用从头读取恢复已取得的范围，并以 `submission_id` 对照本地记录。

## 查询参数

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `subscription_id` | 字符串 | 是 | 有效订阅 ID，不是 Campaign ID。 |
| `limit` | 整数 | 否 | 每页 1–200 条，默认 50。 |
| `from` | 字符串 | 否 | 从头读取时用 `beginning`；省略则为增量。 |
| `cursor` | 字符串 | 否 | 从头读取后续页使用上页返回的 `next_cursor`。 |
| `task_ids` | 字符串 | 否 | 逗号分隔的任务 ID，仅从头读取；缩小订阅范围。 |
| `campaign_ids` | 字符串 | 否 | 逗号分隔的 Campaign ID，仅从头读取；缩小订阅范围。 |

同时给出两类筛选时，两者一起生效。每类最多接收 200 个 ID；当前实现会截断超出的条目，应自行拆分请求。空条目会被忽略，完全为空的筛选表示“不筛选”，不是“返回零条”。范围外的筛选通常返回空页，不会扩大授权。分页时保持原筛选，并原样传回游标。

## 示例：从头读取一页

先安全地设置 `HB_API_KEY` 和本组织正式环境的 `HB_SUBSCRIPTION_ID`：

```sh
curl --silent --show-error --get 'https://api.humanbased.ai/v1/live/pull' \
  --header "Authorization: Bearer $HB_API_KEY" \
  --data-urlencode "subscription_id=$HB_SUBSCRIPTION_ID" \
  --data-urlencode 'from=beginning' \
  --data-urlencode 'limit=5'
```

下例仅展示结构。ID、内容和金额是示例值，不是可下载的样本；实际 `data.items[].data` 取决于交付的输出结构。

```json
{
  "data": {
    "items": [{
      "submission_id": "784210006031",
      "task_id": "784210005001",
      "campaign_id": "784210001001",
      "template_id": "784210002001",
      "data": {"case_id": "returns-014", "decision": "escalate"},
      "quality_score": 0.85,
      "quality_grade": "A",
      "unit_price_usd": 0.02,
      "source": "web",
      "created_at": "2026-10-01T09:30:00+00:00"
    }],
    "next_cursor": null,
    "has_more": false,
    "count": 1,
    "receipt": null
  },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}
```

记录的单价可大于零，而此次重读的回执为空。单价不是本次扣费的证明。

## 成功响应

只有 `success: true` 才能把 `data` 当作成功页。

| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `success` | 布尔值 | 请求是否成功。 |
| `errorCode` | 整数 | 成功时为 `0`。 |
| `errorMessage` | 字符串 | 成功时为 `SUCCESS`；失败时为诊断信息。 |
| `data.items` | 数组 | 本页记录；空数组也是有效结果。 |
| `data.count` | 整数 | 本页记录数。 |
| `data.has_more` | 布尔值 | 读取本页时是否还有下一页。 |
| `data.next_cursor` | 字符串或空值 | 从头读取时的下一页游标。 |
| `data.receipt` | 对象或空值 | 收费增量读取的计量回执；重读时为空。 |

回执存在时，包含 `usage_event_id`、`amount_usd`、`quantity` 和 `balance_available_usd`。把回执与响应一起保存，对账方法见[用量对账](#用量对账)。

### 记录字段

| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `submission_id` | 字符串 | 来源提交 ID；无法取得时可能为空。 |
| `task_id` | 字符串 | 来源任务 ID；可能为空。 |
| `campaign_id` | 字符串 | 来源 Campaign ID；可能为空。 |
| `template_id` | 字符串 | 模板 ID；空值表示未知。 |
| `data` | JSON 值 | 按配置的输出映射交付的内容，不需再次解析为 JSON 字符串。 |
| `quality_score` | 数字 | 从等级映射的分数；没有已识别等级时为 `0`。 |
| `quality_grade` | 字符串 | `S`、`A`、`B`、`C` 或 `D`；无法识别时为空。 |
| `unit_price_usd` | 数字 | 此订阅的单条价格，不是付款回执。 |
| `source` | 字符串 | 来源标签；可能为空。 |
| `created_at` | 字符串 | 原提交时间；可能为空。 |

等级映射为 S → 0.97、A → 0.85、B → 0.70、C → 0.50、D → 0.30。这些数值不是经过校准的概率，也不度量模型效果。分页按交付记录内部顺序；较晚页出现较早的 `created_at` 不足以证明重复。保留 ID 的字符串形式，解析 `data` 中任意精度数字时也应避免精度损失。

<a id="field-schema"></a>
### 获取 `data` 的字段结构

每条记录 `data` 里有哪些字段，取决于它的 `template_id`。下面两个读取接口返回这份字段约定，不必再靠猜测解析记录：

```http
GET /v1/live/task/templates?subscription_id=<SUBSCRIPTION_ID>
GET /v1/live/task/template?template_id=<TEMPLATE_ID>
Authorization: Bearer <API_KEY>
```

复数形式列出该订阅所属活动配置要交付的全部模板，可以在记录到达前先缓存。它返回 `subscription_id`、`count` 和 `templates`；没有配置模板时 `templates` 为空。单数形式按记录中遇到的 `template_id` 查询一个模板，不限于某个订阅。两种形式都需要有数据访问权限的 Key：只用于活动配置的 Key 会得到 `403`。复数形式中，订阅不在 Key 所选范围内时同样返回 `403`；你的组织没有该订阅时返回 `404`。

| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `template_id` | string | 与拉取记录中的 `template_id` 对应 |
| `name` | string | 模板名称 |
| `description` | string | 模板收集的内容 |
| `kind` | string | 模板类型；表单类工作为 `form` |
| `fields` | array | `data` 中每个字段一项：`name`、`type`、`required`、`description`；取值来自固定选项时另有 `enum` |

缺少 `template_id` 或 `subscription_id` 时，请求校验直接返回 `422`，消息写明缺少的参数，没有 `errorCodeName`。带了 `template_id` 但值为空时返回 `400` 和 `TEMPLATE_ID_REQUIRED`；模板不存在时返回 `404` 和 `TEMPLATE_NOT_FOUND`。

## 用量对账

```http
GET /v1/live/usage
Authorization: Bearer <API_KEY>
```

这个接口列出组织已被计费的用量事件，最新的在前。每次返回了记录的收费增量读取产生一条事件，事件的 `usage_event_id` 就是那次读取回执里的值。读取这份列表既不计费，也不移动任何读取位置。

地址和 Key 与 `GET /v1/live/pull` 相同，Key 需要有数据读取权限。列表覆盖该环境中整个组织的用量，Key 自身范围之外的订阅也在其中。接口没有 `subscription_id` 参数。

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `limit` | 整数 | 否 | 返回的事件数。默认 50，最多 200；超过时返回 `422`。 |

接口没有游标，只返回最新的若干条事件。

| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `data.items` | 数组 | 用量事件，最新的在前。 |
| `data.count` | 整数 | `items` 中的事件数。 |
| `usage_event_id` | 字符串 | 事件标识，与被计费那次读取的 `receipt.usage_event_id` 相同。 |
| `subscription_id` | 字符串 | 那次读取使用的订阅。 |
| `dataset_id` | 字符串或空值 | 费用归属的数据集；价格并非来自数据集时为空。 |
| `vertical_id` | 字符串或空值 | 订阅所属的 vertical；没有时为空。 |
| `quantity` | 整数 | 那次读取交付的记录数。 |
| `unit_price_usd` | 数字 | 读取时每条记录的价格。 |
| `amount_usd` | 数字 | 计费金额：`unit_price_usd` 乘以 `quantity`，保留六位小数。 |
| `currency` | 字符串 | `USD`。 |
| `occurred_at` | 字符串 | 计费记录的时间。 |

下面的数值只是示意。随读取响应保存的这份回执：

```json
{
  "usage_event_id": "784210009001",
  "amount_usd": 0.1,
  "quantity": 5,
  "balance_available_usd": 49.9
}
```

对应用量列表里的这条事件：

```json
{
  "data": {
    "items": [
      {
        "usage_event_id": "784210009001",
        "subscription_id": "784210003001",
        "dataset_id": null,
        "vertical_id": "784210004001",
        "quantity": 5,
        "unit_price_usd": 0.02,
        "amount_usd": 0.1,
        "currency": "USD",
        "occurred_at": "2026-10-01T09:31:00+00:00"
      }
    ],
    "count": 1
  },
  "success": true,
  "errorCode": 0,
  "errorMessage": "SUCCESS"
}
```

对账时，用每份回执的 `usage_event_id` 在 `data.items` 中查找，再比较 `quantity` 和 `amount_usd`。

| 你找到 | 说明什么 | 你现在做什么 |
| --- | --- | --- |
| `usage_event_id` 与回执相同的事件 | 这笔费用记录了一次。 | 核对 `quantity` 和 `amount_usd` 与回执一致。 |
| 没有这条事件，且 `count` 等于你传的 `limit` | 事件可能比这次取回的更早。 | 用更大的 `limit` 再请求，最多 200。 |
| 最新 200 条里也没有 | 事件比这个接口能返回的更早。 | 以保存的回执为凭，并在组织的 [Billing 页面](/zh/collaboration/billing)核对该时段的费用。 |
| 有事件却没有对应的回执 | 某次读取已计费，但响应没有保存下来，例如在传输中丢失。 | 按[两种读取模式](#两种读取模式)从头读取，找回那批记录。 |
| 保存的响应里 `receipt: null` | 这次是从头读取，或者没有产生费用。 | 无需对账。 |

<a id="rate-limits"></a>
## 调用频率限制

每个接口对每把 API Key 单独计数，默认每分钟 100 次请求。每个响应都会说明当前额度：

| 响应头 | 含义 |
| --- | --- |
| `X-RateLimit-Limit` | 当前时间窗口允许的请求数 |
| `X-RateLimit-Remaining` | 当前时间窗口剩余的请求数 |
| `Retry-After` | 收到 `429` 时，需要等待的秒数 |

超过限制时，网关返回 `429`，`errorMessage` 为 `Too many requests`，没有 `errorCodeName`。等待 `Retry-After` 给出的秒数后，降低频率继续。分页循环在 `X-RateLimit-Remaining` 降到 `0` 时应先暂停。

## 错误与恢复

同时检查 HTTP 状态和返回外层。`errorCode` 是数字；部分错误有 `errorCodeName`，部分详细错误将结构写入 `errorMessage`，客户端不能假定所有失败具有同一命名字段。

数据接口带详细信息的拒绝，会把详情写成 JSON 字符串放进 `errorMessage`，此时没有 `errorCodeName`。例如余额不足以支付一次计费读取：

```json
{
  "data": null,
  "success": false,
  "errorCode": 402,
  "errorMessage": "{\"error\": \"insufficient_balance\", \"available_usd\": 0.0, \"required_usd\": 0.02}"
}
```

`errorMessage` 以 `{` 开头时，先解析它，再按其中的 `error` 字段分支：`invalid_cursor` 和 `filters_need_reread`（`400`）、`insufficient_balance` 和 `entitlement_required`（`402`，后者附 `dataset_id`），或 `pull_contended` 和 `billing_unavailable`（`503`）。

| HTTP 状态 | 原因示例 | 下一步 |
| --- | --- | --- |
| `400` | 不支持的 `from`、无效订阅或游标，或增量请求带筛选。 | 修正参数，使用接口返回的游标。 |
| `401` | Key 缺失、格式错误、撤销或过期。 | 核对完整 Bearer 凭证或请管理员替换。 |
| `402` | 收费增量读取资金不足。 | 由负责人员核对可用资金。 |
| `402` | 数据按申请定价（`entitlement_required`）。 | 目前没有自助购买方式，充值无效；带上响应中的 `dataset_id` 联系支持。 |
| `403` | 无数据交付权限或订阅不在 Key 范围。 | 核对组织、订阅和获准的 Key。 |
| `404` | 调用方找不到订阅。 | 核对环境、组织和 ID。 |
| `422` | 必填参数缺失或 `limit` 无效。 | 按上表修正请求。 |
| `429` | 用同一把 Key 调用该接口过于频繁。 | 等待 `Retry-After` 给出的秒数，见[调用频率限制](#rate-limits)。 |
| `503` | 计价、余额、计费暂不可用或并发拉取冲突。 | 稍后重试，并按所选读取模式恢复。 |
| `500` 或未知响应 | 服务错误或契约不一致。 | 保存脱敏诊断并联系支持。 |

服务端如返回 `Retry-After`，按其建议重试；重试次数应有限，避免密集轮询。

活动来源的提交必须进入符合条件的交付集合。仅显示审核接受还不够：交付路径还等待奖励授予和交付记录生成，再受订阅和质量筛选限制。此请求是同步响应，不会返回需要轮询的导出任务 ID。到达 `has_more=false` 仅表示完成当前可读范围的遍历，不赋予数据许可或保证永久可用。

支持时请向 [hello@humanbased.ai](mailto:hello@humanbased.ai) 提供环境、请求时间、HTTP 状态和脱敏错误，排除 Key 与私有内容。
