事件接收协议
本页供部署事件接收服务的团队核对请求、响应与重试规则。SDK 接入方只需配置 endpoint 和 projectKey;接收验证见数据校验。
协议要点
- SDK 创建事件时固定身份、Session 和时间戳;接收端不应以接收时的状态覆盖它们。
- 未收到有效整批 ACK 时,SDK 可能重发同一批次。相同事件的
eventId保持不变,普通重试的batchId也保持不变。 - 接收端应按项目和事件 ID 处理重复请求。确认的范围始终是整批,不能按单条事件分别确认。
批量上报请求
请求地址与请求头
POST https://<collector-host>/v1/events/batch
Content-Type: application/json
X-Project-Key: <public-project-key>
X-Idempotency-Key: <batchId>
X-SDK-Name: <sdk-name>
X-SDK-Version: <sdk-version>
X-Protocol-Version: 1.0
Content-Encoding: gzip # 压缩时使用Web 页面离开时还可能发送 text/plain JSON 请求,不带上述自定义请求头。此时 projectKey、idempotencyKey、sdkName、sdkVersion 和 protocolVersion 在查询参数中;idempotencyKey 与正文 batchId 相同。这是尽力发送,不确认本地队列。接收端需单独验证该格式和重复事件处理。
projectKey 是公开项目凭据,不是认证密钥。接收端应自行执行访问控制;客户端请求中不应加入服务端 Secret。
请求体示例
每个 Batch 包含不可变的批次信封与 events 事件列表,单条事件严格遵循 event-envelope.schema.json:
{
"batchId": "batch_01JABC1234567890",
"protocolVersion": "1.0",
"sentAt": 1756100000500,
"events": [
{
"schemaVersion": "1.0",
"eventId": "evt_01JABC1234567890",
"eventName": "order_checkout",
"timestamp": 1756100000000,
"sequence": 42,
"sdk": {
"name": "track-business-android",
"version": "1.0.0"
},
"platform": "android",
"identity": {
"anonymousId": "anon_8f901c2",
"sessionId": "sess_1a2b3c4",
"userId": "user_9527",
"identityEpoch": 1,
"identityVersion": 3,
"stateVersion": 7
},
"consent": {
"level": "STANDARD",
"timestamp": 1756100000000
},
"context": {
"device": {
"manufacturer": "Google",
"model": "Pixel 8",
"osVersion": "14"
},
"app": {
"id": "com.example.app",
"version": "2.1.0"
}
},
"properties": {
"orderId": "ord_9981",
"amount": 199.0
},
"source": "manual",
"capture": {
"mode": "manual"
},
"trackingPlanVersion": "1.0"
}
]
}主要字段
| 字段 | 类型 | 长度 / 范围 | 说明 |
|---|---|---|---|
batchId | String | 8 ~ 128 字符 | 本批次全局唯一标识符,重试保持不变 |
protocolVersion | String | 固定 "1.0" | 契约协议版本号 |
sentAt | Integer | Unix 毫秒时间戳 | 发送端创建批次的时间;可结合接收时间辅助分析,网络延迟会影响偏差判断 |
events | Array | 1 ~ 100 条 | 事件数组,单条大小建议不超过 32 KB |
events[].eventId | String | 8 ~ 128 字符 | 单条事件幂等键;由 SDK 创建或按 API 提供,重试严禁变更,不限定 UUID 格式 |
events[].eventName | String | 1 ~ 128 字符 | 事件名称;命名治理由事件目录或适用的 Tracking Plan 规定 |
events[].timestamp | Integer | Unix 毫秒时间戳 | 事件创建时的 Unix 毫秒时间戳 |
events[].platform | String | 枚举 | 客户端事件为 web / android / ios;服务端事件为 server |
events[].source | String | 枚举 | 采集方式:manual(应用/API 调用)或 auto(SDK/插件采集);无法判断时可省略 |
此页请求示例和 event-envelope.schema.json 描述客户端事件。Node.js、Java、Go、Python 服务端 SDK 使用独立的 server-event-profile.schema.json:其 platform 固定为 server,SDK 语言位于 sdk.language。两种 profile 共用批次传输和整批 ACK 语义;客户端 Schema 的 platform 枚举仍只包含客户端平台。
整批确认
SDK 只有同时收到 HTTP 2xx 和有效响应体,才会确认队列。目标环境应返回以下包装响应:
{
"code": 200,
"data": { "status": "ACCEPTED" },
"message": "success"
}SDK 也接受平铺响应:响应体需包含与请求匹配的 batchId 和 accepted: "all"。
| 结果 | 客户端处理 |
|---|---|
| 有效包装响应或匹配的平铺整批 ACK | 确认本批全部事件。旧逐事件字段和其他扩展元数据不参与 ACK 判定 |
2xx 只有旧 ACK 字段,缺少 accepted: "all" | 不构成有效 ACK;保留整批并按可重试协议错误处理 |
| 空体、无效 JSON 或无法识别的 2xx | 保留整批,按可重试协议错误处理 |
| 408、429、5xx、网络失败 | 保留整批并重试 |
| 不可重试的 4xx | 整批死信 |
| 413 | 多事件批次对半拆分;单条仍超限则本地拒绝 |
包装响应只依据 code=200 与 data.status="ACCEPTED" 确认当前请求批次。message 等附加字段不改变确认结果;成功响应体上限为 1 MiB。详情见可靠投递。
幂等与去重
接收端需验证以下行为:
- 批次幂等键 (
projectKey + batchId):- 相同项目收到相同
batchId的请求时,接收端应根据已记录结果处理重试,避免重复处理; - 若相同
batchId附带不同的请求内容,接收端应返回 409 和BATCH_IDEMPOTENCY_CONFLICT。
- 相同项目收到相同
- 事件幂等键 (
projectKey + eventId):- 若客户端因拆批重发,将已在历史批次成功的
eventId重新提交,接收端应按事件身份设计去重,避免重复统计; - 接收端应结合重试周期确定幂等记录的存储与保留期限;具体实现和可保证的范围由部署方确认。
- 若客户端因拆批重发,将已在历史批次成功的
错误响应与重试
网关在处理连接层异常或全局错误时,返回标准的结构化 JSON 错误模型:
{
"code": "RATE_LIMITED",
"category": "rate_limit",
"retryable": true,
"requestId": "req_01JABC123",
"message": "Tenant rate limit exceeded"
}408、429 和 5xx 会保留整批并退避重试;429 可用 Retry-After 指定等待时间。413 会拆分多事件批次,拆分后每个子批使用新 batchId,原事件 eventId 不变。其他不可重试的 4xx 将整批移入死信。HTTP 2xx 仍必须满足上述 ACK 规则。
画像与实体操作
机器可校验的请求结构源自 profile-operations.schema.json。SDK 将画像与 Item 操作编码为 $profile_* / $item_* 事件,并与普通事件共用 Store、批次上传、整批 ACK 和重试路径。若部署端实现这些操作的投影,应按其后端操作合同保证相应更新语义;SDK 收到整批 ACK 只表示 Collector 接受该批,不证明画像或 Item 投影已完成。
| 事件标识 | 操作意图 | 部署端实现责任 |
|---|---|---|
$profile_set | 覆盖更新指定属性 | 后写覆盖已有同名属性,未提及属性完整保留 |
$profile_set_once | 首次初始化赋值 | 仅在属性尚不存在时写入;已有属性严格保持不变 |
$profile_increment | 数值字段累加 | 部署端按原子累加语义更新;存储实现由部署端决定,非数值输入按操作合同拒绝 |
$profile_append | 数组属性追加 | 每个值为最多 100 项的同类型字符串或数值数组;请求 Schema 不规定接收端的去重策略 |
$profile_unset | 删除指定属性键 | 属性移除,调用保持幂等 |
$profile_delete | 删除画像属性记录 | 仅在部署端支持该操作时生效;不代表历史事件、备份或身份图谱的法定擦除流程 |
$item_set / $item_delete | Item 属性操作 | 操作以 itemType 与 itemId 标识对象;存储隔离和生命周期由部署端定义 |
接收端验收
部署接收服务前,建议核对:
- [ ] 项目路由与隔离:按目标环境接收
X-Project-Key及适用的?projectKey=传参,验证项目路由、访问控制与租户数据隔离;公开projectKey本身不是服务端认证凭据; - [ ] Schema 强校验:请求体合法性基于
event-envelope.schema.json强校验,非法属性被安全过滤或警告; - [ ] 整批确认:成功时返回
code: 200与data.status: "ACCEPTED";若使用兼容的平铺成功体,须包含匹配的batchId和accepted: "all"。旧acceptedEventIds/rejected/unresolved不能单独确认。对无效、重复或受限事件的处理和诊断需在目标环境确认; - [ ] 幂等去重:确认接收端按
batchId和eventId实现所需的去重语义及保留期限,并在 ACK 丢失后的重试场景验证; - [ ] 限流防护:在触发 429 限流时,返回合法的
Retry-After秒级响应头; - [ ] 安全审计脱敏:网关链路日志中绝不打印用户密码、Token、未脱敏手机号或完整事件业务正文。