Files
story/docs/设备端注册绑定接口.md
2026-09-12 08:57:29 +08:00

799 lines
32 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 设备端注册、绑定与激活接口说明
更新日期:2026-09-12。
依据原始《AI电话_首次配网注册与激活_完整流程与详细设计(1)》附件 A、B、C、E、I 整理,仅列设备直接调用 SaaS 的 C01~C11。按本次要求省去辅助流程控制字段,不添加原文没有规定的请求或响应字段。
本文件保留字段列表、中文含义、获取来源和示例。服务端已按此接口契约实现,版本为 `ONBOARDING-1.1`,并按本次要求移除三个辅助流程控制字段。实现与验证记录见[接口对齐说明](设备端接口_文档与当前代码差异.md);线上发布结果另见[发布记录](onboarding-release-20260912.md)。
## 通用规则
- 设备已完成生产登记,持有出厂写入的 `device_id`、32 字节 `device_key`、`key_version`。
- 全部接口使用 POST,正文为 JSON,不带 query 参数;普通报文上限 8 KiB,配置包上限 32 KiB。
- C01 是无可信时间时可通过专用 HTTP 入口调用的校时接口。其余接口使用严格验证证书和主机名的 HTTPS。
- `<…>` 为占位值,必须使用实际返回值或生成值;示例中的编号、版本、时间不是可直接请求线上服务的数据。
- `string` 为字符串,`integer` 为整数,`boolean` 为 true/false,`object` 为 JSON 对象。UUID 按原文为标准 36 字符 UUID v4;Base64URL 无填充编码末尾不带 `=`。
- 版本、计数按原文范围为 0~2147483647,时间为 Unix 毫秒。字段表中的全部输入均为原文必填字段。
### 请求头
C02~C11 使用下列认证头。C01 的设备编号和密钥版本放在正文中,不要求先提交可信时间戳或普通设备请求签名。
| 字段 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- |
| `Content-Type` | 正文格式 | 固定 `application/json`,C01 也使用。 |
| `X-Device-ID` | 生产设备编号 | 从出厂配置读取,例如 `KP01-AABBCCDDEEFF`。 |
| `X-Key-Version` | 设备认证密钥版本 | 从本机已保存凭据读取,十进制填写。 |
| `X-Timestamp-Ms` | 本次发送时间 | C01 验证并校时后,读取当前可信 UTC 毫秒时间。 |
| `X-Nonce` | 本次发送随机数 | 每次 HTTP 发送生成新的 16 字节安全随机数,做 Base64URL 无填充编码。 |
| `X-Request-ID` | 业务请求编号 | 由设备生成 UUID v4;同一次业务操作重试保留,原始正文也保留。 |
| `Authorization` | 设备请求认证码 | 使用本机 `device_key` 计算签名,填写 `Device-HMAC-SHA256 <64位小写十六进制HMAC>`。 |
签名按附件 B 的 DEVREQ1 规则计算,以下 9 行以 LF 连接,末尾不再加换行:
```text
DEVREQ1
<大写HTTP方法>
<实际path>
<device_id>
<key_version十进制>
<timestamp_ms十进制>
<nonce的Base64URL>
<request_id>
<原始请求正文的SHA256小写十六进制>
```
密钥使用原始 32 字节,不发送密钥本身。网络重试更新随机数、发送时间和签名,但不重新生成同一操作的业务编号、按键事件编号或执行编号。
### 普通响应外层
C02~C11 使用此封装,C01 不使用。成功时读取 `data`,失败时读取 `code`、`message`;设备归属和激活结果以业务状态为准。
| 字段 | 类型 | 中文含义 | 来源/怎么用 |
| --- | --- | --- | --- |
| `ok` | boolean | 本次请求是否成功 | 平台生成;不能单独用它判断设备已完成绑定或激活。 |
| `code` | string | 业务结果码 | 平台返回,成功为 `OK`,失败为对应错误码。 |
| `request_id` | string(UUID) | 请求追踪编号 | 平台返回与本次请求关联的编号,供查找请求记录。 |
| `data` | object | 成功业务输出 | 字段按各接口下表读取。 |
| `server_time_ms` | integer | 平台时间 | 平台生成;原文成功响应中提供,不能替代 C01 的首次可信校时。 |
| `message` | string | 错误中文说明 | 失败时由平台提供,便于提示原因。 |
```json
{
"ok": true,
"code": "OK",
"request_id": "<请求UUID>",
"data": {},
"server_time_ms": 1789092000000
}
```
失败示例:
```json
{
"ok": false,
"code": "CLAIM_EXPIRED",
"request_id": "<请求UUID>",
"message": "绑定确认已过期,请重新发起"
}
```
### 公共设备状态 `state`
下表相对于 `data.state`。按本次删减后的口径列出,状态版本、绑定代数和摘要必须来自同一版返回状态。
| 字段 | 类型 | 中文含义 | 来源/后续使用 |
| --- | --- | --- | --- |
| `device_id` | string | 生产设备编号 | 来自平台生产记录,设备与本机编号核对。 |
| `device_revision` | integer | 平台设备状态版本 | 由平台维护;后续用于 C05、C07、C11。 |
| `binding_generation` | integer | 绑定代数 | 平台随绑定关系变化而更新,初始 0,首次绑定后 1;用于防止旧归属信息被继续使用。 |
| `binding_state` | string | 绑定状态 | 平台根据归属关系返回,取值见下表。 |
| `activation_state` | string | 激活状态 | 平台根据激活任务返回,只有 READY 表示服务已就绪。 |
| `state_hash` | string | 本版状态摘要 | 平台按附件 I.1 计算;设备保存该版状态后在 C07 原样回传。 |
| 字段 | 取值及中文含义 |
| --- | --- |
| `binding_state` | `UNBOUND` 未绑定;`CLAIM_PENDING` 等待按键;`BOUND` 已绑定;`TRANSFER_PENDING` 转移处理中。 |
| `activation_state` | `NOT_STARTED` 未开始;`RESERVED` 已预留;`CONTACT_POSSIBLE` 可能已执行;`REGISTERED_OBSERVED` 已确认注册;`READY` 已就绪;`RECONCILE_REQUIRED` 待对账;`BLOCKED` 被阻止。 |
`state_hash` 按原文对以下内容以 LF 连接后计算 SHA256,输出小写十六进制,末尾无换行:
```text
STATE1
<device_id>
<device_revision>
<binding_generation>
<binding_state>
<activation_state>
```
`device_revision` 是平台状态版本,`binding_generation` 是归属代数,`local_generation` 是设备本地保存序号,`config_version` 是激活配置版本;不能互相代填。
## 接口总览
下面路径前统一加 `/api/v1/device`。
| 编号 | POST 路径 | 用途 |
| --- | --- | --- |
| C01 | `/time-challenges` | 校时并获取注册挑战 |
| C02 | `/register` | 认证设备并登记启动 |
| C03 | `/setup-sessions` | 创建设置会话 |
| C04 | `/handoffs` | 获取可交给手机的一次性绑定入口 |
| C05 | `/poll` | 查询状态及命令 |
| C06 | `/claim-confirmations` | 设备按键确认绑定 |
| C07 | `/state-acks` | 确认已保存绑定状态 |
| C08 | `/activation-attempts` | 申请一次 SDK 执行许可 |
| C09 | `/activation-results` | 上报激活执行结果 |
| C10 | `/activation-acks` | 确认激活结果已保存 |
| C11 | `/setup-close` | 记录设置收尾并关闭入口 |
## C01 校时并取得注册挑战
`POST /api/v1/device/time-challenges`
用途:让设备在时钟不正确时先取得可信时间,再使用严格 HTTPS。只返回时间和挑战,不返回用户资料、登录令牌或火山密钥。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `device_id` | string | 是 | 生产设备编号 | 读取出厂配置:`KP01-` 加 12 位大写十六进制 MAC。 |
| `key_version` | integer | 是 | 认证密钥版本 | 读取本机保存的凭据版本,不是密钥内容。 |
| `boot_id` | string(UUID) | 是 | 本次启动编号 | 设备每次启动生成并保留,后续 C02、C03、C05 沿用。 |
| `client_nonce` | string | 是 | 本次校时随机数 | 设备生成 16 字节安全随机数并做 Base64URL 编码,每次校时重试换新值。 |
输入示例:
```json
{
"device_id": "KP01-AABBCCDDEEFF",
"key_version": 1,
"boot_id": "<本次启动UUID>",
"client_nonce": "<16字节随机数的Base64URL>"
}
```
### 输出字段
下表为校时响应根对象。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `payload_b64` | string | 校时数据包 | 平台将下方六个字段序列化并编码;设备用原字符串验签后解码。 |
| `mac_hex` | string | 校时数据认证码 | 平台用本设备密钥计算 TIME1 HMAC,设备使用同一密钥验证。 |
输出示例:
```json
{
"payload_b64": "<校时数据包的Base64URL>",
"mac_hex": "<64位小写十六进制HMAC>"
}
```
`payload_b64` 验签后解码字段:
| 字段 | 类型 | 中文含义 | 来源/怎么用 |
| --- | --- | --- | --- |
| `device_id` | string | 设备编号回显 | 核对等于本次请求及本机编号。 |
| `boot_id` | string(UUID) | 启动编号回显 | 核对等于设备本次启动编号。 |
| `client_nonce` | string | 随机数回显 | 核对等于本次新请求的随机数。 |
| `challenge_id` | string(UUID) | 注册挑战编号 | 由平台生成并返回,保存后传入 C02。 |
| `server_time_ms` | integer | 经过认证的平台时间 | 平台可信时钟生成;全部校验通过后设置设备时钟。 |
| `expires_at_ms` | integer | 挑战到期时间 | 平台签发时间加 120 秒,到期后重新 C01。 |
```json
{
"device_id": "KP01-AABBCCDDEEFF",
"boot_id": "<本次启动UUID>",
"client_nonce": "<本次请求随机数>",
"challenge_id": "<注册挑战UUID>",
"server_time_ms": 1789092000000,
"expires_at_ms": 1789092120000
}
```
校验公式:`HMAC-SHA256(device_key, UTF8("TIME1\n" + payload_b64))`,其中 `\n` 表示一个 LF 换行。附件 B 要求设备核对全部回显字段、单调计时 RTT 不超过 10 秒、时间不早于固件或已保存可信时间允许的下限;通过后校时,再开始 HTTPS。
## C02 设备认证与启动登记
`POST /api/v1/device/register`
用途:认证已有设备身份并登记本次启动;重复请求不新建设备、不清空既有归属。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `challenge_id` | string(UUID) | 是 | 注册挑战 | 取 C01 验签后解码对象中的 `challenge_id`。 |
| `boot_id` | string(UUID) | 是 | 本次启动编号 | 沿用同一次 C01 的 `boot_id`。 |
| `hardware_model` | string,≤32字节 | 是 | 设备型号 | 从出厂配置读取,按真实型号填写。 |
| `hardware_revision` | string,≤16字节 | 是 | 硬件版本 | 读取出厂记录的硬件修订号。 |
| `firmware_version` | string,≤32字节 | 是 | 固件版本 | 读取设备当前实际运行的固件版本。 |
| `sdk_version` | string,≤64字节 | 是 | SDK版本 | 读取设备实际集成的 SDK 版本信息。 |
输入示例:
```json
{
"challenge_id": "<C01挑战UUID>",
"boot_id": "<本次启动UUID>",
"hardware_model": "KP01",
"hardware_revision": "A1",
"firmware_version": "1.0.0",
"sdk_version": "1.0.0"
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `state` | object | 当前设备状态 | 平台返回已有记录的最新状态,完整字段见公共状态表。 |
| `contract_version` | string | 协议版本 | 原文规定 `ONBOARDING-1.1`;用于确认两侧接口口径。 |
输出示例:
```json
{
"state": {
"device_id": "KP01-AABBCCDDEEFF",
"device_revision": 3,
"binding_generation": 0,
"binding_state": "UNBOUND",
"activation_state": "NOT_STARTED",
"state_hash": "<本版状态SHA256摘要>"
},
"contract_version": "ONBOARDING-1.1"
}
```
## C03 创建设置会话
`POST /api/v1/device/setup-sessions`
用途:为本次设置建立平台会话;已绑定设备可恢复上下文,但不能借此重新认领。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `boot_id` | string(UUID) | 是 | 本次启动编号 | 沿用已完成 C02 的启动编号。 |
| `local_setup_id` | string(UUID) | 是 | 本地设置编号 | 设备为本次设置流程生成;重试和 C04 沿用。 |
| `transport_profile` | string | 是 | 本地交互方式 | 按原文固定传 `LOCAL_WEB`。 |
输入示例:
```json
{
"boot_id": "<本次启动UUID>",
"local_setup_id": "<设备生成的本地设置UUID>",
"transport_profile": "LOCAL_WEB"
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `setup_id` | string(UUID) | 平台设置会话编号 | 平台生成,后续传 C04、C05、C11;不同于设备生成的 local_setup_id。 |
| `expires_at_ms` | integer | 设置会话到期时间 | 平台生成;默认 15 分钟,原文绝对上限 30 分钟,设备以返回期限为准。 |
| `poll_after_ms` | integer | 建议轮询间隔,毫秒 | 平台返回;绑定期间原文默认 2000。 |
| `state` | object | 当前设备状态 | 平台返回,完整字段见公共状态表。 |
输出示例:
```json
{
"setup_id": "<平台设置会话UUID>",
"expires_at_ms": 1789092900000,
"poll_after_ms": 2000,
"state": {
"device_id": "KP01-AABBCCDDEEFF",
"device_revision": 3,
"binding_generation": 0,
"binding_state": "UNBOUND",
"activation_state": "NOT_STARTED",
"state_hash": "<本版状态SHA256摘要>"
}
}
```
## C04 取得一次性手机入口
`POST /api/v1/device/handoffs`
用途:设备取得 SaaS 绑定入口,并交给当前已授权的本地会话。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `setup_id` | string(UUID) | 是 | 平台设置会话编号 | 取 C03 的 `data.setup_id`。 |
| `local_setup_id` | string(UUID) | 是 | 本地设置编号 | 沿用 C03 请求中的同名字段。 |
| `code_challenge` | string | 是 | 入口校验摘要 | 设备生成32字节随机值并编码为 verifier;对 verifier 的 ASCII 字节算 SHA256,再编码为 Base64URL。 |
输入示例:
```json
{
"setup_id": "<C03设置会话UUID>",
"local_setup_id": "<C03本地设置UUID>",
"code_challenge": "<32字节SHA256结果的Base64URL>"
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `handoff_token` | string | 一次性入口令牌 | 平台生成32字节随机值并编码;设备把它放进入口链接。 |
| `expires_at_ms` | integer | 令牌到期时间 | 平台生成,原文有效期90秒;不能由设备自行延长。 |
| `entry_url` | string | SaaS绑定页面地址 | 由平台固定配置的 HTTPS 白名单入口。 |
输出示例:
```json
{
"handoff_token": "<平台一次性令牌>",
"expires_at_ms": 1789092090000,
"entry_url": "https://saas.example/onboarding/entry"
}
```
设备自己保留 `verifier`,不是向平台多传一个字段。交给手机的完整链接为:
```text
<entry_url>#token=<handoff_token>&verifier=<设备生成的verifier>
```
## C05 查询状态和命令
`POST /api/v1/device/poll`
用途:设备查询当前状态,领取绑定确认命令或激活配置;查询本身不代表用户按键或 SDK 执行完成。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `setup_id` | string(UUID) | 是 | 平台设置会话编号 | 取 C03 返回的设置会话编号。 |
| `boot_id` | string(UUID) | 是 | 本次启动编号 | 沿用本次已经注册的启动编号。 |
| `known_revision` | integer | 是 | 设备已知状态版本 | 取最近返回的 `state.device_revision`;首次不知道时传0。按原文该字段必填。 |
输入示例:
```json
{
"setup_id": "<C03设置会话UUID>",
"boot_id": "<本次启动UUID>",
"known_revision": 3
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `state` | object | 当前设备状态 | 平台返回,完整字段见公共状态表。 |
| `command` | object 或 null | 当前命令 | 平台产生;没有命令时为 null,原文兼容 NONE 类型。命令子字段见下表。 |
| `poll_after_ms` | integer | 下次轮询间隔,毫秒 | 由平台返回;绑定阶段默认2秒,普通待机不保持高频轮询。 |
输出示例:
```json
{
"state": {
"device_id": "KP01-AABBCCDDEEFF",
"device_revision": 3,
"binding_generation": 0,
"binding_state": "UNBOUND",
"activation_state": "NOT_STARTED",
"state_hash": "<本版状态SHA256摘要>"
},
"command": null,
"poll_after_ms": 2000
}
```
### `command` 命令字段
| 字段 | 类型 | 中文含义 | 来源/怎么用 |
| --- | --- | --- | --- |
| `command_id` | string(UUID) | 命令编号 | 平台生成,设备识别重复命令;原文未规定它必须等于申请或激活任务编号。 |
| `type` | string | 命令类型 | 原文为 NONE、CONFIRM_CLAIM、APPLY_ACTIVATION、UPDATE_STATE、CLOSE_SETUP;按返回类型处理。 |
| `expires_at_ms` | integer | 命令到期时间 | 平台生成,设备按可信时间判断有效性。 |
| `payload` | object | 命令数据 | 由平台生成;下面只展开原文明确给出的两种命令内容。 |
`CONFIRM_CLAIM` 的 `payload`:
| 字段 | 类型 | 中文含义 | 来源/怎么用 |
| --- | --- | --- | --- |
| `claim_id` | string(UUID) | 绑定申请编号 | 平台产生,设备从此处获取后传 C06。 |
| `challenge` | string | 按键挑战 | 平台生成32字节随机值编码,真实按键后 C06 原样回传。 |
| `binding_digest` | string | 固定绑定内容摘要 | 平台计算64位 SHA256 小写十六进制,设备不改写,C06 原样回传。 |
| `masked_account` | string | 脱敏账号提示 | 平台生成,例如“尾号1234”,供用户确认当前申请。 |
| `expected_binding_generation` | integer | 预期绑定代数 | 平台在建立申请时确定;C06 原样回传。 |
```json
{
"command_id": "<命令UUID>",
"type": "CONFIRM_CLAIM",
"expires_at_ms": 1789092120000,
"payload": {
"claim_id": "<绑定申请UUID>",
"challenge": "<32字节随机值的Base64URL>",
"binding_digest": "<固定绑定内容SHA256>",
"masked_account": "尾号1234",
"expected_binding_generation": 0
}
}
```
`APPLY_ACTIVATION` 的 `payload` 按原文只列配置封装:
| 字段 | 类型 | 中文含义 | 来源/怎么用 |
| --- | --- | --- | --- |
| `config_b64` | string | 激活配置包 | 平台生成配置 JSON 并编码;设备验签后解码,下方列出解码字段。 |
| `config_mac` | string | 配置认证码 | 平台计算 `HMAC-SHA256(device_key, UTF8("CFG1\n" + config_b64))` 的十六进制值;其中 \n 表示一个 LF,设备用本机密钥验证。 |
配置包验签解码后的字段,依据附件 E.2:
| 字段 | 类型 | 中文含义 | 来源/怎么用 |
| --- | --- | --- | --- |
| `contract_version` | string | 配置协议版本 | 原文为 `ONBOARDING-1.1`。 |
| `device_id` | string | 配置所属设备 | 平台读取生产记录,设备核对与本机一致。 |
| `activation_id` | string(UUID) | 激活任务编号 | 平台创建并保留;设备从配置包中取出,后续 C08、C09、C10 使用。 |
| `config_version` | integer | 激活配置版本 | 平台生成;后续 C08、C10 使用,不自行写死。 |
| `binding_generation` | integer | 配置对应绑定代数 | 平台维护;与当前公共状态核对,防止使用旧归属配置。 |
| `expires_at_ms` | integer | 配置到期时间 | 由平台签发,设备不得用过期配置开始新的首次激活;原文未在此规定统一24小时有效期。 |
| `iot` | object | 供应商注册资料 | 平台组装,子字段如下。 |
| `iot.instance_id` | string | 供应商实例编号 | 来自平台保存的实例配置。 |
| `iot.product_key` | string | 供应商产品标识 | 来自平台保存的产品配置。 |
| `iot.product_secret` | string | 受保护的产品注册密钥 | 这是原文 E.2 明确列出的字段;设备通过受保护配置取得,供原文 SDK 注册流程使用;与当前代码方案的差异见单独说明。 |
| `iot.device_name` | string | 物理设备在供应商侧的固定名称 | 由 SaaS 首次激活前生成并永久记录,设备原样使用;不随账号、重试、重启更换。 |
| `bot_id` | string | 共享智能体编号 | 来自平台配置,设备按对应 SDK 要求使用。 |
原文要求配置仅经设备 HTTPS 下发,落地使用加密 NVS,不交给手机或写入普通日志。该表没有增加当前代码中的其他运行配置字段。
## C06 设备按键确认绑定
`POST /api/v1/device/claim-confirmations`
用途:用户真实按下设备确认键后,设备提交本次确认,平台正式建立归属关系。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `claim_id` | string(UUID) | 是 | 绑定申请编号 | 取 C05 同一确认命令的 `payload.claim_id`。 |
| `challenge` | string | 是 | 按键挑战 | 取该命令的 `payload.challenge`,原样回传。 |
| `binding_digest` | string,64位hex | 是 | 绑定内容摘要 | 取该命令同名字段,原样回传。 |
| `expected_binding_generation` | integer | 是 | 预期绑定代数 | 取该命令同名字段,不能自行加一。 |
| `press_event_id` | string(UUID) | 是 | 本次真实按键事件编号 | 设备在真实按键发生时生成并保存,网络重试沿用。 |
输入示例:
```json
{
"claim_id": "<C05申请UUID>",
"challenge": "<C05按键挑战>",
"binding_digest": "<C05绑定摘要>",
"expected_binding_generation": 0,
"press_event_id": "<设备实际按键UUID>"
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `claim_id` | string(UUID) | 已处理的绑定申请 | 平台返回,设备核对是本次申请。 |
| `state` | object | 处理后的归属及激活状态 | 平台返回;保存对应版本、代数和摘要后调用 C07。 |
| `already_applied` | boolean | 该确认是否已经提交过 | 由平台识别重复相同申请的结果;不可据此产生第二次绑定。 |
输出示例:
```json
{
"claim_id": "<绑定申请UUID>",
"state": {
"device_id": "KP01-AABBCCDDEEFF",
"device_revision": 4,
"binding_generation": 1,
"binding_state": "BOUND",
"activation_state": "RESERVED",
"state_hash": "<本版状态SHA256摘要>"
},
"already_applied": false
}
```
## C07 确认绑定状态已保存
`POST /api/v1/device/state-acks`
用途:报告设备已经保存平台状态,不根据此回执修改 owner。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `device_revision` | integer | 是 | 已保存状态版本 | 取同一版 `state.device_revision`。 |
| `binding_generation` | integer | 是 | 已保存绑定代数 | 取同一版 `state.binding_generation`。 |
| `local_generation` | integer | 是 | 本地保存序号 | 由设备本地保存模块产生并在保存成功后记录,不用平台版本号代替。 |
| `state_hash` | string,64位hex | 是 | 已保存状态摘要 | 取同一版 `state.state_hash` 并核对实际保存内容。 |
输入示例:
```json
{
"device_revision": 4,
"binding_generation": 1,
"local_generation": 1,
"state_hash": "<已保存状态摘要>"
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `accepted_revision` | integer | 平台接受的保存版本 | 平台确认后返回,设备可记录该版已完成回执。 |
| `setup_can_close` | boolean | 是否可以收尾 | 平台根据回执和流程条件判断;设备按结果执行收尾。 |
输出示例:
```json
{
"accepted_revision": 4,
"setup_can_close": false
}
```
原文收尾条件:页面与设备都确认后等待3秒,或设备确认后最多30秒。这里保留原文的布尔判断,不新增等待时间或后续动作字段。
## C08 申请 SDK 执行许可
`POST /api/v1/device/activation-attempts`
用途:平台先记录本次可能执行的事实,再决定是否允许本次 SDK 调用,避免不确定结果下重复执行。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `activation_id` | string(UUID) | 是 | 激活任务编号 | 从 C05 激活配置包解码后的 `activation_id` 取得。 |
| `config_version` | integer | 是 | 本次使用的配置版本 | 从同一份已验证配置的 `config_version` 取得。 |
| `attempt_id` | string(UUID) | 是 | 一次 SDK 执行编号 | 设备为本次实际执行生成并保留;本次请求重试沿用,不重新申请第二次执行。 |
输入示例:
```json
{
"activation_id": "<配置包中的激活任务UUID>",
"config_version": 1,
"attempt_id": "<设备生成的执行UUID>"
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `allowed` | boolean | 是否允许本次执行 | 平台判断后返回;只有 true 才能开始此次 SDK 调用。 |
| `attempt_id` | string(UUID) | 许可对应执行编号 | 平台返回本次执行编号,后续 C09 使用。 |
| `permit_expires_at_ms` | integer | 许可到期时间 | 原文 I.1 规定许可有效期30秒;超窗重新查询,不能推断先前调用一定没有执行。 |
| `state` | object | 当前设备状态 | 平台返回,完整字段见公共状态表。 |
输出示例:
```json
{
"allowed": true,
"attempt_id": "<本次执行UUID>",
"permit_expires_at_ms": 1789092030000,
"state": {
"device_id": "KP01-AABBCCDDEEFF",
"device_revision": 5,
"binding_generation": 1,
"binding_state": "BOUND",
"activation_state": "CONTACT_POSSIBLE",
"state_hash": "<本版状态SHA256摘要>"
}
}
```
## C09 上报激活执行结果
`POST /api/v1/device/activation-results`
用途:上报已获许可的实际 SDK 执行结果。超时或无法确定结果时上报 UNKNOWN,不能当作明确失败重复执行。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `activation_id` | string(UUID) | 是 | 激活任务编号 | 沿用 C08 所属任务编号。 |
| `attempt_id` | string(UUID) | 是 | 实际执行编号 | 沿用 C08 已申请并返回的执行编号,不在上报时重新生成。 |
| `stage` | string | 是 | 执行阶段 | 按原文 I.1 填 `SDK_REGISTER` 或 `CONVERSATION_TEST`,由设备依据本次实际执行操作确定。 |
| `sdk_result` | integer | 是 | SDK返回码 | 读取本次实际 SDK 调用或回调结果,不编造结果码。 |
| `outcome` | string | 是 | 本次结果分类 | 原文取值:`SUCCESS_OBSERVED` 已观察到成功、`DEFINITE_FAILURE` 明确失败、`UNKNOWN` 未知。 |
输入示例:
```json
{
"activation_id": "<激活任务UUID>",
"attempt_id": "<C08执行UUID>",
"stage": "SDK_REGISTER",
"sdk_result": 0,
"outcome": "SUCCESS_OBSERVED"
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `activation_id` | string(UUID) | 结果所属任务 | 平台返回,设备与本次任务核对。 |
| `state` | object | 处理后的设备状态 | 平台返回,判断仍在激活、待处理或已 READY。 |
| `reconcile_required` | boolean | 是否需要对账 | 平台根据本次结果判定;true 时不要自行开始第二次同类激活执行。 |
输出示例:
```json
{
"activation_id": "<激活任务UUID>",
"state": {
"device_id": "KP01-AABBCCDDEEFF",
"device_revision": 6,
"binding_generation": 1,
"binding_state": "BOUND",
"activation_state": "REGISTERED_OBSERVED",
"state_hash": "<本版状态SHA256摘要>"
},
"reconcile_required": false
}
```
首次通话测试时使用 `stage=CONVERSATION_TEST`。结果分类只表示这一次执行的事实,不等同于设备最终状态;服务是否可用仍读取 `state.activation_state`。
## C10 确认激活结果已保存
`POST /api/v1/device/activation-acks`
用途:设备确认激活结果已在本地保存;重复相同回执不产生新的激活副作用。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `activation_id` | string(UUID) | 是 | 已保存结果所属任务 | 沿用配置包及 C09 对应的激活任务编号。 |
| `local_generation` | integer | 是 | 本地保存序号 | 由设备保存模块在本次结果保存成功后记录,不使用配置版本代替。 |
| `config_version` | integer | 是 | 结果对应配置版本 | 沿用本次激活实际使用的配置包版本。 |
输入示例:
```json
{
"activation_id": "<激活任务UUID>",
"local_generation": 2,
"config_version": 1
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `accepted` | boolean | 回执是否被接受 | 由平台验证后返回。 |
| `state` | object | 当前设备状态 | 平台返回,完整字段见公共状态表。 |
输出示例:
```json
{
"accepted": true,
"state": {
"device_id": "KP01-AABBCCDDEEFF",
"device_revision": 6,
"binding_generation": 1,
"binding_state": "BOUND",
"activation_state": "REGISTERED_OBSERVED",
"state_hash": "<本版状态SHA256摘要>"
}
}
```
## C11 设置流程收尾
`POST /api/v1/device/setup-close`
用途:请求或报告本次设置流程收尾。关闭临时入口不清空已提交的绑定或激活任务。
### 输入字段
| 字段 | 类型 | 必填 | 中文含义 | 怎么获得/填写 |
| --- | --- | --- | --- | --- |
| `setup_id` | string(UUID) | 是 | 要关闭的设置会话 | 取本次 C03 返回的 `setup_id`。 |
| `reason` | string | 是 | 关闭原因 | 设备按实际情况填 SUCCESS、TIMEOUT、CANCELLED 或 NETWORK_SWITCH。 |
| `final_revision` | integer | 是 | 关闭前最后收到的状态版本 | 取最后成功收到的 `state.device_revision`,不是设备本地保存序号。 |
输入示例:
```json
{
"setup_id": "<C03设置会话UUID>",
"reason": "SUCCESS",
"final_revision": 6
}
```
### 输出字段
下表字段位于普通响应的 `data` 中。
| 字段 | 类型 | 中文含义 | 来源/设备怎么用 |
| --- | --- | --- | --- |
| `close_allowed` | boolean | 是否允许关闭 | 平台判定并返回,设备据此执行关闭。 |
| `state` | object | 当前设备状态 | 平台返回并保留正式绑定及激活事实,便于后续恢复。 |
输出示例:
```json
{
"close_allowed": true,
"state": {
"device_id": "KP01-AABBCCDDEEFF",
"device_revision": 6,
"binding_generation": 1,
"binding_state": "BOUND",
"activation_state": "REGISTERED_OBSERVED",
"state_hash": "<本版状态SHA256摘要>"
}
}
```
## 字段从哪里取得
| 要使用的字段 | 取值来源 |
| --- | --- |
| `device_id`、`device_key`、`key_version` | 设备出厂写入的身份及凭据。 |
| `boot_id` | 设备每次启动生成。 |
| `challenge_id` | C01 验签后的校时包。 |
| `local_setup_id` | 设备为一次本地设置生成。 |
| `setup_id` | C03 平台返回。 |
| `claim_id`、`challenge`、`binding_digest`、预期绑定代数 | 同一条 C05 确认命令,不跨命令拼接。 |
| `press_event_id` | 设备真实按键时生成。 |
| `activation_id`、`config_version` | C05 激活配置包验签解码后取得。 |
| `attempt_id` | 设备为一次 SDK 执行生成,C08 申请、C09 沿用。 |
| `local_generation` | 设备保存模块记录本地保存完成的序号。 |
| `device_revision`、`binding_generation`、`state_hash` | 平台返回的同一版公共状态。 |
## 平台如何确定 C08 执行阶段
C08 不增加阶段字段。平台按任务中已确认的事实判定:尚未观察到注册成功时,许可用于 `SDK_REGISTER`;已注册且 C10 确认保存后,许可用于 `CONVERSATION_TEST`。C09 的阶段必须匹配平台为该执行编号记录的阶段。已完成 READY 的同代绑定不再发放首次激活许可。
原文未给出部分命令的完整 payload,当前实际下发 `CONFIRM_CLAIM`、`APPLY_ACTIVATION` 两种命令,其余保留枚举不作为新增接口功能。
来源:[原始流程文档](<D:/wechat/xwechat_files/wangcong3092_983b/msg/file/2026-09/AI电话_首次配网注册与激活_完整流程与详细设计(1).docx>),重点是附件 B(认证及校时)、C(设备接口)、E(状态和配置)、I.1(逐接口输入与输出)。