便于阅读的译文 — 以英文版本为准。
状态:草案 v0.1 · 一份开放、厂商中立的规范 · 许可:开放(见 §10)
OTEP 是一个面向任何可追踪对象生命周期的开放、厂商中立的协议。 它定义了统一的事件模型和统一的状态词汇表,使得来自任何来源的追踪事件——自有车队配送、第三方快递、承运商面单及更多场景——都能够被交换、理解,并投射到国际标准,而无需为每一方重新做集成。
本文档即为该规范:包括结构、字段、状态表、状态机、外部标准映射,以及构建合规实现所需的一致性规则。它与具体实现无关——它描述的是协议本身,而非任何特定厂商的内部实现。RFC-2119 关键词(MUST / SHOULD / MAY)具有规范性。
追踪是碎片化的:每家承运商对字段和状态码的命名各不相同,每个配送渠道以自己的形态上报,而对接全球标准则意味着一次又一次地重新集成。OTEP 为生产者和消费者提供了一种统一的共同语言:生产者只需发出一次 OTEP 事件,每个 OTEP 消费者都能理解它,并能将其投射到自己所需的标准。
时间线是一个信封(envelope),承载被追踪的对象以及一组有序的事件。
{
"otep_version": "0.1",
"profile": "parcel", // domain profile (§8)
"subject": {
"tracking_number": "SR123...", // ≥1 identifier required
"order_id": 12345, // optional
"package_id": 67890, // optional
"external_tracking_number": "1Z...",// optional
"gs1_sscc": "00...", // optional — enables EPCIS epcList
"piece_id": "..." // optional — enables ONE Record linkage
},
"current_status": "delivered", // projection of the latest event
"delivered": true,
"events": [ /* OTEP events, §4 */ ]
}
{
// WHAT — carried once at the timeline level (subject above)
// WHEN
"occurred_at": "2026-06-10T09:30:00-04:00", // event instant, ISO-8601 with offset
"recorded_at": "2026-06-10T09:45:23-04:00", // ingestion instant (optional)
"time_type": "actual", // actual | estimated | scheduled
// WHERE
"location": {
"name": "Toronto Hub",
"code": "YYZ-2",
"gln": "0614141000005", // GS1 GLN → EPCIS SGLN
"lat": 43.6777, "lng": -79.6248,
"country": "CA" // ISO 3166-1 alpha-2
},
// WHY / WHAT HAPPENED
"status_code": "out_for_delivery", // an OTEP status code (§4)
"phase": "out_for_delivery", // derived from status_code
"incident_reason": null, // an OTEP incident reason (§4) when exception
// WHO
"actor": { "type": "driver", "name": "Jane D.", "phone": "+1..." },
// PROVENANCE
"source": {
"type": "self_delivery", // self_delivery | third_party_delivery | carrier_label
"provider_id": null,
"carrier_code": null,
"external_event_code": null, // your raw status code (preserve it)
"raw": { /* original payload */ }
},
// PROOF
"pod": {
"photos": [ { "url": "...", "content_base64": null } ],
"signature": [ { "url": "...", "content_base64": null } ],
"recipient": "John Smith"
}
}
除 subject、occurred_at、status_code 和 source 之外的每个字段都是可选的——部分来源只填充其拥有的内容。节点/枢纽扫描会将 location 设为执行扫描的设施。高频 GPS 遥测数据不是 OTEP 事件;事件是在状态发生变化或发生节点扫描时发出的。
20 个规范化的生命周期代码。phase 始终可由代码推导得出。
| code | phase | 终态 | POD | 含义 |
|---|---|---|---|---|
information_submitted |
pre_shipment | 已收到订单信息 | ||
booking_confirmed |
pre_shipment | 承运商/预订已确认 | ||
awaiting_pickup |
pre_shipment | 待揽收 | ||
out_for_pickup |
pickup | 正在前往揽收途中 | ||
picked_up |
pickup | ✓ | 已从发货方揽收 | |
pickup_failed |
exception | 揽收尝试失败 | ||
pickup_rescheduled |
exception | 将重新尝试揽收 | ||
received |
inbound | 已在设施收货 | ||
arrival_scan |
inbound | 节点到达扫描 | ||
in_transit |
transit | 运输中 | ||
package_outbound |
transit | 已离开设施 | ||
removed_from_route |
exception | 已从路线中移除 | ||
route_cancelled |
exception | 路线已取消 | ||
out_for_delivery |
out_for_delivery | 已在配送车辆上 | ||
delivered |
delivered | ✓ | ✓ | 已送达收货人 |
delivery_failed |
exception | 配送尝试失败 | ||
delivery_rescheduled |
exception | 将重试/重新配送 | ||
return_to_sender |
return | ✓ | 正在退回原点 | |
rejected_by_recipient |
return | ✓ | 收件人拒收 | |
cancelled |
return | ✓ | 订单已取消 |
pre_shipment · preparing · pickup · inbound · in_custody · transit ·
out_for_delivery · delivered · exception · return。(preparing 和 in_custody
保留给非包裹类的 profile 使用——§8。)
source.type:self_delivery · third_party_delivery · carrier_label。
time_type:actual · estimated · scheduled(默认 actual)。
当事件处于 exception 阶段时,它 SHOULD 携带一个来自以下规范化词汇表的 incident_reason:
carrier_damaged_parcel、carrier_sorting_error、carrier_address_not_found、
carrier_parcel_lost、carrier_not_enough_time、carrier_vehicle_issue、
carrier_capacity_exceeded、carrier_mechanical_delayretailer_cancelled、retailer_incorrect_data、retailer_not_ready、
retailer_incorrect_parcel、retailer_incorrect_dimensions、retailer_packaging_issueconsignee_refused、consignee_business_closed、consignee_not_available、
consignee_not_home、consignee_cancelled、consignee_verification_failed、
consignee_incorrect_address、consignee_access_restricted、consignee_safe_place_unavailablecustoms_delay、customs_documentation、customs_duties_unpaid、
customs_prohibited、customs_inspectionweather_delay、natural_disaster、force_majeureparcel_being_researched、security_issue、regulatory_hold、unknown阶段向前推进;异常会中断流程并解析后回退。终态(delivered、
return_to_sender、rejected_by_recipient、cancelled)关闭该对象。
pre_shipment → preparing → pickup → inbound → in_custody → transit → out_for_delivery → delivered ✓
└──────────── exception ──────────┘
└──────────── return / cancelled ✓
规则:
occurred_at 对事件排序,并且 MUST 容忍乱序到达。OTEP 的四个维度与主流标准逐字段对齐,因此每个标准都是 OTEP 事件的一种输出投射。
| OTEP | GS1 EPCIS 2.0 | IATA ONE Record | UN/CEFACT |
|---|---|---|---|
occurred_at (+offset) |
eventTime + eventTimeZoneOffset |
eventDate + eventTimeType=Actual |
Event Date/Time |
recorded_at |
recordTime |
recordedAt | — |
| subject (sscc/piece) | epcList (SSCC URN) |
linkedObject → Piece/Shipment |
Consignment |
location (gln/lat/lng) |
readPoint / bizLocation (SGLN) |
recordedAtLocation → Location |
Location |
status_code |
bizStep + disposition |
eventCode |
Transport status code |
actor |
sourceList / extension | Actor / Party | Party |
incident_reason |
disposition / ErrorDeclaration | event remark | Status reason code |
各代码对应的取值(EPCIS CBV bizStep/disposition、ONE Record eventCode、UN/CEFACT 代码)发布在机器可读的代码本(codebook)中。除了这些国际标准之外,一条 OTEP 时间线还可以投射到 OpenTelemetry 追踪、OGC SensorThings 观测,以及常见的电商平台(AfterShip、Shopify、Amazon、Walmart、BigCommerce、Magento、WooCommerce、Etsy)。
置信度:EPCIS CBV 取值是稳定的标准 URN。ONE Record 和 UN/CEFACT 的代码取值是针对最后一公里的最佳匹配,在对外使用前应对照官方代码列表进行校验。"送达收货人"没有完全精确的 CBV
bizStep——使用最接近的匹配(receiving+received),或使用用户词汇表扩展 URN。
OTEP 通过一个小型的只读 HTTP 接口集进行消费;所有端点均为公开的。
| 方法 | 路径 | 返回 |
|---|---|---|
| GET | /api/v1/otep/trackings/{tracking_number} |
某追踪号对应的时间线 |
| GET | /api/v1/otep/trackings/{tracking_number}/events |
仅返回事件 |
| POST | /api/v1/otep/trackings/batch |
一次调用查询多个追踪号 |
| POST | /api/v1/otep/validate |
对提交的时间线进行一致性校验(§9) |
同时还提供一个暴露相同时间线的 GraphQL 查询。
通过 ?format= 查询参数或 Accept profile,同一条时间线会被序列化为你所请求的任意一种表示形式:
| 请求 | 表示形式 |
|---|---|
?format=otep(默认) |
原生 OTEP 时间线 |
?format=epcis |
GS1 EPCIS 2.0(JSON-LD ObjectEvent) |
?format=onerecord |
IATA ONE Record(LogisticsEvent) |
?format=uncefact |
UN/CEFACT 运输状态 |
?format=otlp |
OpenTelemetry 追踪 |
?format=sensorthings |
OGC SensorThings 观测 |
?format=aftership | shopify | amazon | walmart | bigcommerce | magento | woocommerce | etsy |
对应平台的追踪/履约形态 |
在某个投射中无法被赋予代码的事件会被跳过并计数(绝不会被静默丢弃)。
OTEP 是一个通用协议,而非仅限于包裹的协议。协议层(事件信封、阶段主干、状态机)是通用的;而具体的状态码归属于在时间线上通过 profile 声明的某个 profile。§4 中的代码属于 parcel profile。其他领域——搬家、餐饮配送、仓储及更多——在各自的 profile 下添加自己的代码集,以 otep:<profile>:<code> 命名空间标识,每个代码都向上映射到同一套阶段主干。新增一个 profile 是一种扩展,而非协议变更。
当一个生产者或消费者发出或接受的每个事件都满足以下表格和规则时,它就是 OTEP-conformant(OTEP 一致的)。
| 字段 | 类型 | 要求 | 约束 |
|---|---|---|---|
otep_version |
string | MUST | semver,例如 0.1 |
profile |
string | MUST | 已注册的 profile |
subject |
object | MUST | §9.2 |
current_status |
string|null | SHOULD | 一个状态码(§4) |
current_phase |
string|null | SHOULD | 若两者都存在,则 MUST 等于 current_status 的 phase |
delivered |
boolean | SHOULD | 当且仅当 current_status = delivered 时为 true |
events |
array | MUST | 事件对象(§9.3),可按 occurred_at 排序 |
subjecttracking_number / order_id / package_id 中至少一个 MUST 存在。
| 字段 | 类型 | 约束 |
|---|---|---|
tracking_number |
string | 非空 |
order_id / package_id |
integer|null | |
external_tracking_number |
string|null | |
gs1_sscc |
string|null | 18 位数字 |
piece_id |
string|null |
| # | 字段 | 类型 | 要求 | 约束 |
|---|---|---|---|---|
| 1 | occurred_at |
string | MUST | 带偏移量的 ISO-8601 |
| 2 | recorded_at |
string|null | SHOULD | ISO-8601 |
| 3 | time_type |
string | MAY(默认 actual) |
actual | estimated | scheduled |
| 4 | status_code |
string|null | MUST¹ | §4.1 中的一个代码 |
| 5 | phase |
string|null | SHOULD | MUST 等于 status_code 的 phase |
| 6 | incident_reason |
string|null | SHOULD² | §4.4 中的一个原因 |
| 7 | description |
string|null | MAY | 人类可读 |
| 8 | location |
object|null | MAY | §9.4 |
| 9 | actor |
object|null | MAY | { type, name?, phone? };type ∈ {driver, operator, carrier, system} |
| 10 | source |
object | MUST | §9.5 |
| 11 | pod |
object|null | MAY³ | { photos[], signature[], recipient? } |
¹ 一个已编码的事件 MUST 携带一个来自 §4.1 的状态码。一次你尚无法分类的原始扫描 MAY 将 status_code = null,但 MUST 在 source.external_event_code 中保留原生代码,并 MUST 被计数,绝不丢弃。
² 处于 exception 阶段的事件 SHOULD 携带一个 incident_reason。
³ status_code ∈ {delivered, picked_up} 的事件 SHOULD 携带一个 pod。
locationname(string)· code(string)· gln(GS1 GLN,13 位数字)· lat / lng(WGS-84)·
country(ISO 3166-1 alpha-2)。全部可选。
source| 字段 | 类型 | 要求 | 约束 |
|---|---|---|---|
type |
string | MUST | self_delivery | third_party_delivery | carrier_label |
provider_id |
integer|null | MAY | |
carrier_code |
string|null | MAY | |
external_event_code |
string|null | SHOULD⁴ | 你的原始状态码 |
raw |
object|null | MAY | 原始负载 |
⁴ 当 status_code 为 null 时 MUST 存在,以确保原生代码不会丢失。
occurred_at MUST 能解析为 ISO-8601。在摄取时将数字纪元时间戳和 .NET /Date(ms)/ 归一化;不要发出这些形式。occurred_at 排序、容忍乱序到达,并基于(subject、status_code、occurred_at)去重。phase、current_status、current_phase、delivered 都是投射——若存在,则它们 MUST 与事件时间线保持一致。校验你的输出:将一条时间线 POST 到 POST /api/v1/otep/validate。将任何 errors 视为阻断性问题;处理 warnings。还发布了机器可读的 JSON Schema 和完整的代码本(每个状态码、阶段和外部映射),用于离线校验。
OTEP 是一份开放规范,任何一方均可自由实现。
otep:<profile>:<code> 寻址,阶段以 otep:phase:<name> 寻址。一旦在某个已发布版本中发布,标识符的含义即不可变。otep_version)。x- 前缀(otep:parcel:x-my_code),直到正式注册为止。OTEP 欢迎其他厂商带来各自的追踪事件标准,以便 OTEP 能与之互操作,两个方向皆可:
source.external_event_code 中;未映射的代码会被计数,绝不丢弃。即使你无法直接采用 OTEP,也欢迎你在自己的 API 响应中添加一个归一化的 otep_status,并分享你的追踪事件代码以用于对照映射。请以针对本规范提案的方式(而非分叉)来提出映射,使实现方收敛趋同;新格式会接入同一套 ?format= 内容协商机制,且绝不会破坏任何现有的消费者。