Webhook 集成指南
Webhook 是什么?
Webhook 就是 Superroute 在事件发生时(订单创建、配送完成、跟踪更新等),向您配置的 URL 发起的一次 HTTP POST 请求。您搭建一个接收端点,我们把事件推送过去。
投递机制
事件入队后异步发送。每次请求都带 HMAC-SHA256 签名,您可以验证它确实来自我们。投递失败(非 2xx 响应或超时)时会按指数退避自动重试,最多 5 次。
安全模型
您在设置页配置一个共享密钥,每条出站 webhook 都用它签名。接收端用相同密钥重新计算签名并对比 — 一致即代表内容真实未被篡改。
事件目录
一共八种出站事件类型。每种事件在设置页都有独立的 URL 字段,您可以按需订阅任意子集。
本地配送订单(送货 D / 取件 P / 点对点 P2P)创建时触发,覆盖所有入口:网页表单、REST/GraphQL API、电商平台同步、自动规则、导入行等。Label-service 及其他非配送类型订单不触发。如果同一收件方同时配置了 order_create_async_postback_url,批量导入流程中将跳过此 webhook(由批量回调统一通知)。对应字段 order_create_webhook_url。
载荷示例
{
"result": true,
"id": 1001,
"ref": "REF-001",
"type": "D",
"customer_id": 42,
"tracking_number": [
"SR000000001"
],
"shipping_price": "13.30",
"price_details": {
"shipping_fee": "10.00",
"signature_fee": "1.50",
"sub_total": "11.50",
"tax_details": [
{
"tax_number_id": 3,
"tax_name": "HST",
"tax_rate": 13,
"tax": "1.50"
}
],
"tax_zone_id": 7
},
"currency": "CAD",
"packages": [
{
"id": 2001,
"ref": "PKG-001",
"tracking_number": "SR000000001",
"external_tracking_number": "1Z999AA10123456784"
}
]
}
订单状态每次变更时触发 — 取件、运输中、已送达、异常、取消等。对应字段 order_status_change_webhook_url。
载荷示例
{
"id": 50001,
"user_id": 3011,
"business_id": 42,
"location_id": 12,
"location_information": {
"name": "Toronto Depot",
"address_1": "10 King St",
"address_2": "",
"city": "Toronto",
"province": "ON",
"country": "Canada",
"postcode": "M5V1A1",
"territory_id": 7,
"territory_name": "Downtown"
},
"grid_id": null,
"ip_address": "203.0.113.24",
"operation_category": 1,
"operation_type": 1001,
"is_ascan": 0,
"operation_description": null,
"route_id": 8801,
"order_id": 1001,
"before_status": 20,
"after_status": 8,
"tracking_event_id": 90001,
"gps_tracking_id": 77012,
"latitude": "43.6532",
"longitude": "-79.3832",
"order_address_history_id": null,
"created_at": "2026-09-16 11:37:15",
"updated_at": "2026-09-16 11:37:15",
"action": "created",
"operation_type_text": "driver change status",
"operation_category_text": "driver",
"order_ref": "REF-001",
"package_id": 2001,
"tracking_number": "SR000000001",
"external_tracking_number": "1Z999AA10123456784",
"type": "D",
"return_reason": "",
"shipping_to": {
"name": "Jane Doe",
"company_name": "",
"telephone": "+14165550123",
"email": "jane@example.com",
"address_1": "55 Queen St W",
"address_2": "Unit 8",
"city": "Toronto",
"province": "ON",
"country": "Canada",
"code": "",
"postcode": "M5H2M9"
},
"shipping_from": {
"name": "Toronto Depot",
"company_name": "Acme Logistics",
"telephone": "+14165550100",
"address_1": "10 King St",
"address_2": "",
"city": "Toronto",
"province": "ON",
"country": "Canada",
"code": "",
"postcode": "M5V1A1"
}
}
包裹生命周期事件触发时(提交信息、开始配送、配送成功、未送达等)。对应字段 tracking_event_webhook_url。「已送达」与「已取件」事件还会附带签收凭证:proof_files 以及 proof_files_detail(file_id、type、url、full_url、带签名的下载地址)。事件之后才上传的照片通过 pod.files_updated 推送。每个文件还附带对应事件信息:tracking_event_id、tracking_event_status_id、tracking_event_key、service_type(1 = delivery / 2 = pickup)和 service_status(1 = success / 2 = failed);未记录事件的历史文件这些字段为 null。
载荷示例
{
"id": 90001,
"order_id": 1001,
"order_ref": "REF-001",
"location_id": 12,
"location_name": "Toronto Depot",
"tracking_event_type": "D",
"tracking_event_status_id": 500,
"tracking_event_key": "deliver_success",
"otep_status": "delivered",
"proof_files": [
"storage/2026-07-20/1001_S_Ab3kQ9xYzLm4Np.jpeg",
"storage/2026-07-20/1001_P_Cd7rT2wVuKe8Hs.jpeg"
],
"proof_files_detail": [
{
"file_id": 234567,
"type": 1,
"url": "storage/2026-07-20/1001_S_Ab3kQ9xYzLm4Np.jpeg",
"full_url": "https://api.superroute.ca/storage/2026-07-20/1001_S_Ab3kQ9xYzLm4Np.jpeg",
"signed_url": "https://api.superroute.ca/files/pod/234567?expires=1784748600&signature=8f2c1d...",
"signed_url_expires_at": 1784748600,
"tracking_event_id": 90001,
"tracking_event_status_id": 500,
"tracking_event_key": "deliver_success",
"service_type": 1,
"service_status": 1
},
{
"file_id": 234568,
"type": 2,
"url": "storage/2026-07-20/1001_P_Cd7rT2wVuKe8Hs.jpeg",
"full_url": "https://api.superroute.ca/storage/2026-07-20/1001_P_Cd7rT2wVuKe8Hs.jpeg",
"signed_url": "https://api.superroute.ca/files/pod/234568?expires=1784748600&signature=41ba90...",
"signed_url_expires_at": 1784748600,
"tracking_event_id": 90001,
"tracking_event_status_id": 500,
"tracking_event_key": "deliver_success",
"service_type": 1,
"service_status": 1
}
],
"description": {
"en": "Your parcel has been delivered successfully. Thank you!",
"fr": "Votre colis a été livré avec succès. Merci !",
"chs": "您的包裹已成功送达,感谢您的使用!",
"cht": "您的包裹已成功送達,感謝您的使用!",
"es": "Tu paquete ha sido entregado con éxito. ¡Gracias!",
"de": "Ihr Paket wurde erfolgreich zugestellt. Vielen Dank!",
"nl": "Uw pakket is succesvol afgeleverd, bedankt voor uw gebruik!",
"pt": "Seu pacote foi entregue com sucesso. Obrigado!",
"it": "Il tuo pacco è stato consegnato con successo. Grazie!",
"sr": "Vaš paket je uspešno dostavljen, hvala vam što koristite naše usluge!",
"hu": "Csomagja sikeresen kiszállításra került, köszönjük hogy használta szolgáltatásunkat!",
"pl": "Twoja paczka została dostarczona pomyślnie. Dziękujemy!",
"sk": "Váš balík bol úspešne doručený. Ďakujeme!",
"cs": "Váš balík byl úspěšně doručen. Děkujeme!"
},
"tracking_number": [
"SR000000001"
],
"external_tracking_number": [
"1Z999AA10123456784"
]
}
批量订单导入处理完成后触发一次,载荷包含每行的处理结果数组。对应字段 order_create_async_postback_url。
载荷示例
[
{
"result": true,
"id": 1001,
"ref": "REF-001",
"type": "D",
"customer_id": 42,
"tracking_number": [
"SR000000001"
],
"shipping_price": "13.30",
"currency": "CAD",
"packages": [
{
"id": 2001,
"ref": "PKG-001",
"tracking_number": "SR000000001",
"external_tracking_number": "1Z999AA10123456784"
}
]
},
{
"result": false,
"message": "Please provider a valid postcode",
"ref": "REF-002"
}
]
签收照片或签名被新增、替换或删除时触发(action:added / updated / removed)——每个文件一次推送,无需再轮询附件。配置 pod_files_webhook_url 后启用。每个文件还附带对应事件信息:tracking_event_id、tracking_event_status_id、tracking_event_key、service_type(1 = delivery / 2 = pickup)和 service_status(1 = success / 2 = failed);未记录事件的历史文件这些字段为 null。
载荷示例
{
"action": "added",
"order_id": 1001,
"order_ref": "REF-001",
"tracking_number": [
"SR000000001"
],
"external_tracking_number": [
"1Z999AA10123456784"
],
"file": {
"file_id": 234567,
"type": 1,
"note": null,
"url": "storage/2026-07-20/1001_S_Ab3kQ9xYzLm4Np.jpeg",
"full_url": "https://api.superroute.ca/storage/2026-07-20/1001_S_Ab3kQ9xYzLm4Np.jpeg",
"signed_url": "https://api.superroute.ca/files/pod/234567?expires=1784748600&signature=8f2c1d...",
"signed_url_expires_at": 1784748600,
"tracking_event_id": 90001,
"tracking_event_status_id": 500,
"tracking_event_key": "deliver_success",
"service_type": 1,
"service_status": 1
},
"event_id": "9b2e4c7a-1f30-4d8e-8a55-6c0f1e2d3a4b",
"event_time": "2026-09-16T11:37:15-04:00",
"event_timestamp": 1789573035
}
订单被永久删除时触发,便于你的系统同步移除。配置 order_deleted_webhook_url 后启用。
载荷示例
{
"action": "deleted",
"order_id": 1001,
"order_ref": "REF-001",
"type": "D",
"orders_status_id": 2,
"tracking_number": [
"SR000000001"
],
"external_tracking_number": [
"1Z999AA10123456784"
],
"event_id": "4c1a7f92-0b6d-4e11-9c3a-2f7b5d8e6a10",
"event_time": "2026-09-16T11:37:15-04:00",
"event_timestamp": 1789573035
}
取消请求被拒绝时触发(例如订单已在派送中),便于你的运营管道监控失败的取消,无需轮询 API 响应。配置 order_cancel_failed_webhook_url 后启用。
载荷示例
{
"result": false,
"code": "ORDER_ALREADY_IN_DELIVERY",
"message": "Order can no longer be cancelled at its current status",
"http_status": 409,
"submitted": {
"order_id": 1001,
"tracking_number": "SR000000001",
"external_tracking_number": null
}
}
线路看板席位易主或看板状态变化时触发——action 字段说明发生了什么(claimed、standby、pooled、promoted、withdrawn、vetoed、replaced、assigned、awarded、lost、displaced、settled、board_opened、board_closed、board_cancelled)。仅限企业级配置。通过 route_board_webhook_url 订阅。
载荷示例
{
"event": "route_board.seat_changed",
"action": "claimed",
"board": {
"id": 12,
"title": "Thursday AM routes",
"date": "2026-09-10",
"mode": "route",
"status": "open",
"business_id": 7,
"broker_id": null
},
"seat": {
"id": 301,
"route_id": 940,
"slot_id": 950,
"route_name": "North Loop",
"work_from": "08:00",
"work_end": "12:00",
"status": "filled"
},
"claim": {
"id": 5001,
"driver_id": 920,
"driver_name": "Alex Driver",
"driver_alias": "AD",
"rank": 1,
"status": "primary",
"bid_price": null,
"awarded_price": null,
"reason": null
},
"occurred_at": "2026-09-16T11:37:15-04:00",
"extra": {}
}
供应商智能柜事件
面向对接智能柜的第三方配送供应商的独立 webhook 通道。事件投递到你供应商账号配置的端点,每个端点可订阅任意事件子集。
已开门 — 投柜尝试的柜门打开瞬间触发——无论司机是在柜机屏幕上输入送件码开门,还是通过远程开门 API 开门——换格重开也会触发。opening 数据块列出每个已打开格口的 grid_id、硬件门板号 compartment_number 和自提柜格口号 pickup_locker_number(按列从上到下、再从左到右的显示序号)。每个格口还带 pickup_locker_code,即收件人要去的「{货架编码}-{格口号}」标签;格口没有格口号时为 null。负载中还包含 pickup_code——收件人取件码,开门瞬间即已分配;司机确认放入后仍是同一个码,但只有在确认放入之后才能用于取件。
载荷示例
{
"event_id": "PLE-01JABCDEE",
"event_type": "partner_locker.delivery.doors_opened",
"livemode": true,
"occurred_at": "2026-07-21T14:29:40-04:00",
"data": {
"delivery_no": "PLD-100234",
"external_order_id": "EXT-9001",
"status": "awaiting_confirmation",
"delivery_mode": "standard",
"pickup_code": "483920",
"opening": {
"attempt_id": 501,
"attempt_no": 1,
"opened_count": 2,
"failed_count": 0,
"partial": false,
"groups": [
{
"shelf_name": "LK-A1",
"locker_identifier": "SL-88231",
"compartments": [
{ "package_sequence": 1, "compartment_number": "12", "grid_id": 5011, "pickup_locker_number": 3, "pickup_locker_code": "LK-A1-3" },
{ "package_sequence": 2, "compartment_number": "15", "grid_id": 5014, "pickup_locker_number": 6, "pickup_locker_code": "LK-A1-6" }
]
}
]
}
}
}
已投柜 — 投柜确认、包裹放入柜中后触发。载荷中包含收件人取件码。每个格口还带 pickup_locker_code,即收件人要去的「{货架编码}-{格口号}」标签;格口没有格口号时为 null。通过远程开门 API 打开的柜门,柜机上报全部已开格口关门后由平台自动结算,因此无需调用 confirm 也会触发本事件;confirmed_by 标明结算路径:courier_terminal、partner_api、door_close、timeout_door_closed 或 console。
载荷示例
{
"event_id": "PLE-01JABCDEF",
"event_type": "partner_locker.delivery.delivered",
"livemode": true,
"occurred_at": "2026-07-21T14:30:00-04:00",
"data": {
"delivery_no": "PLD-100234",
"external_order_id": "EXT-9001",
"status": "delivered",
"pickup_status": "ready",
"requested_package_count": 2,
"actual_package_count": 2,
"failed_package_count": 0,
"picked_up_package_count": 0,
"partial_delivery": false,
"delivery_mode": "standard",
"confirmed_by": "door_close",
"split_from_delivery_no": null,
"split_delivery_no": null,
"pickup_code": "483920",
"location": { "id": 12, "name": "Main St Locker" },
"packages": [
{ "sequence": 1, "external_package_id": "PKG-1", "status": "confirmed", "shelf_id": 21, "grid_id": 5011, "compartment_number": "12", "pickup_locker_number": 3, "pickup_locker_code": "LK-A1-3" },
{ "sequence": 2, "external_package_id": "PKG-2", "status": "confirmed", "shelf_id": 21, "grid_id": 5014, "compartment_number": "15", "pickup_locker_number": 6, "pickup_locker_code": "LK-A1-6" }
]
}
}
已取件 — 收件人取走已寄存包裹后触发。
载荷示例
{
"event_id": "PLE-01JABCDEG",
"event_type": "partner_locker.pickup.completed",
"livemode": true,
"occurred_at": "2026-07-22T09:12:00-04:00",
"data": {
"delivery_no": "PLD-100234",
"status": "picked_up",
"pickup_status": "completed",
"picked_up_package_count": 2
}
}
投递失败 — 投递失败时触发;包含每个包裹的失败代码。
载荷示例
{
"event_id": "PLE-01JABCDEH",
"event_type": "partner_locker.delivery.failed",
"livemode": true,
"occurred_at": "2026-07-21T14:35:00-04:00",
"data": {
"delivery_no": "PLD-100235",
"status": "failed",
"failed_package_count": 1,
"packages": [
{ "sequence": 1, "status": "failed", "failure_code": "NO_CAPACITY" }
]
}
}
已过期 — 未使用的送件码或未取件的寄存超过有效期时触发。
载荷示例
{
"event_id": "PLE-01JABCDEI",
"event_type": "partner_locker.delivery.expired",
"livemode": true,
"occurred_at": "2026-07-24T00:05:00-04:00",
"data": {
"delivery_no": "PLD-100236",
"status": "expired"
}
}
已取消 — 投递在完成前被取消时触发。
载荷示例
{
"event_id": "PLE-01JABCDEJ",
"event_type": "partner_locker.delivery.cancelled",
"livemode": true,
"occurred_at": "2026-07-21T15:00:00-04:00",
"data": {
"delivery_no": "PLD-100237",
"status": "cancelled"
}
}
修正重开门 — 在纠错窗口内重新打开已寄存格口以修正放错位置时触发——可在柜机屏幕或通过 API 操作。correction 块中列出被重开的格口。每个格口还带 pickup_locker_code,即收件人要去的「{货架编码}-{格口号}」标签;格口没有格口号时为 null。
载荷示例
{
"event_id": "PLE-01JABCDEK",
"event_type": "partner_locker.delivery.correction_reopened",
"livemode": true,
"occurred_at": "2026-07-21T14:31:05-04:00",
"data": {
"delivery_no": "PLD-100234",
"status": "delivered",
"correction": {
"channel": "device",
"reopened_count": 2,
"failed_count": 0,
"compartments": [
{ "compartment_number": "A03", "package_sequence": 1, "grid_id": 5011, "pickup_locker_number": 3, "pickup_locker_code": "LK-A1-3" },
{ "compartment_number": "A04", "package_sequence": 2, "grid_id": 5014, "pickup_locker_number": 6, "pickup_locker_code": "LK-A1-6" }
]
}
}
}
partner_locker_delivery.event_type_code_rotated — 合作伙伴更换投递的送件码或取件码时触发。rotation 数据块说明换的是哪种码、何时更换、是否重发了收件人通知——新码本身绝不通过 webhook 传递,只在换码 API 的直接响应中揭示一次。
载荷示例
{
"event_id": "PLE-01JABCDEM",
"event_type": "partner_locker.delivery.code_rotated",
"livemode": true,
"occurred_at": "2026-08-11T09:15:00-04:00",
"data": {
"delivery_no": "PLD-100234",
"external_order_id": "EXT-9001",
"status": "active",
"rotation": {
"code_type": "access",
"rotated_at": "2026-08-11T09:15:00-04:00",
"recipient_notified": false
}
}
}
签名与校验: 供应商智能柜 webhook 使用独立的签名方案:X-Webhook-Signature 为 base64(HMAC-SHA256(密钥, 时间戳 + "\n" + 投递 ID + "\n" + 原始报文)),时间戳与投递 ID 取自 X-Webhook-Timestamp 和 X-Webhook-Delivery-Id 请求头。同时请校验 X-Webhook-Content-Digest(报文的 SHA-256)并拒绝过期时间戳。X-Webhook-Id 在重试之间保持不变——可用于幂等去重。
如何配置: 端点在「第三方配送 → 供应商智能柜 → 设置」中管理,每个供应商一个端点,可勾选事件列表。投递失败按指数退避最多重试 7 次后进入死信;死信事件可在事件页面手动重发。
沙盒(模拟柜机)事件: 针对模拟柜机创建的投递会产生与生产环境完全相同的 Webhook 事件,并使用相同密钥签名,方便您以真实流量进行开发联调。沙盒事件通过三种方式标记:载荷中携带 "livemode": false,event_id 以 PLE-MOCK- 开头,请求头包含 X-Webhook-Test: 1。若端点配置了沙盒地址,沙盒事件将发送到该地址而非生产地址;未配置时回退到生产地址,但仍带有标记。「投递沙盒事件」开关可完全停止沙盒事件的外发。
第三方配送事件
推送给第三方配送服务商(快递商)的包裹配送 Webhook。覆盖配送任务的整个生命周期,快递商无需再轮询新任务。该类别与下方的智能柜事件相互独立:每个服务商按类别分别配置独立的端点、签名密钥和事件订阅——可在其自己的服务商门户中配置,也可由平台运营方配置。
任务已创建 — 当订单被分配给服务商时触发——无论是通过自动规则还是手动分配。载荷包含任务编号、订单标识和包裹跟踪号。
载荷示例
{
"event_id": "TPDE-01JABCDEA",
"event_type": "delivery.assignment.created",
"occurred_at": "2026-07-30T09:15:00-04:00",
"data": {
"assignment_no": "TPD20260730091500ABC123",
"assignment_status": "assigned",
"order_id": 88231,
"order_ref": "SO-100234",
"packages": [
{ "tracking_number": "TRK-1001", "external_tracking_number": "EXT-1001" }
]
}
}
包裹已移交 — 当仓库将该任务的全部包裹实际移交给服务商时触发。
载荷示例
{
"event_id": "TPDE-01JABCDEB",
"event_type": "delivery.assignment.handed_over",
"occurred_at": "2026-07-30T14:02:00-04:00",
"data": {
"assignment_no": "TPD20260730091500ABC123",
"assignment_status": "accepted",
"order_id": 88231,
"order_ref": "SO-100234",
"external_order_number": "CARRIER-556",
"packages": [
{ "tracking_number": "TRK-1001", "external_tracking_number": "EXT-1001" }
]
}
}
任务已取消 — 当平台从服务商处撤回任务时触发。reason 字段区分 cancelled(任务在承运商处被取消)、fallback_to_self_delivery(平台将订单收回自行配送)和 reassigned(订单已改派给其他服务商)。
载荷示例
{
"event_id": "TPDE-01JABCDEC",
"event_type": "delivery.assignment.cancelled",
"occurred_at": "2026-07-30T16:40:00-04:00",
"data": {
"assignment_no": "TPD20260730091500ABC123",
"assignment_status": "cancelled",
"order_id": 88231,
"order_ref": "SO-100234",
"reason": "cancelled"
}
}
部分送达 — 当一票货件中部分包裹已送达、其余仍在途时触发。packages 数组给出每个包裹的结局,legs 列出在承运商处下的第三方订单——承运商不支持一票多件时,每个包裹一个订单。
载荷示例
{
"event_id": "TPDE-01JABCDED",
"event_type": "delivery.assignment.partial_delivered",
"occurred_at": "2026-07-31T11:20:00-04:00",
"data": {
"assignment_no": "TPD20260730091500ABC123",
"assignment_status": "accepted",
"order_id": 88231,
"order_ref": "SO-100234",
"packages": [
{ "tracking_number": "TRK-1001", "outcome": "delivered" },
{ "tracking_number": "TRK-1002" }
],
"legs": [
{ "leg_no": 1, "package_id": 4451, "external_order_number": "CARRIER-556", "push_status": "pushed", "status": "accepted", "outcome": "delivered" },
{ "leg_no": 2, "package_id": 4452, "external_order_number": "CARRIER-557", "push_status": "pushed", "status": "accepted" }
]
}
}
签名与校验: 第三方配送 Webhook 使用与供应商智能柜 webhook 相同的签名方案:X-Webhook-Signature 为 base64(HMAC-SHA256(secret, timestamp + "\n" + delivery id + "\n" + raw body)),其中 timestamp 和 delivery id 取自 X-Webhook-Timestamp 和 X-Webhook-Delivery-Id 请求头。同时请校验 X-Webhook-Content-Digest(报文的 SHA-256)并拒绝过期时间戳。X-Webhook-Id 在重试之间保持不变——可用于幂等去重。
如何配置: 服务商可在服务商门户(Webhook 设置)中自行配置该端点,平台运营方也可在「第三方配送 → 服务商 → Webhook」中配置。每个服务商一个端点,可勾选事件列表。签名密钥可自动生成,也可自定义设置,并可在设置页面查看。投递失败按指数退避最多重试 7 次后进入死信;死信事件可手动重发。设置页面可随时发送带签名的测试(mock)事件——测试请求携带 X-Webhook-Test: 1 请求头,且 payload data 中包含 "test": true。
订单生命周期事件
在经典的 order.status_change Webhook(保持不变)之外提供的细粒度、可选事件:分配给了谁、司机是否接受、包裹何时被取件、在途、送达或失败,以及司机上下班变化和经过节流的司机位置。在您配置下方 URL 之前不会发送任何内容。
订单已分配给司机(手动、线路规划或自动分配)。由编排器分配时 data.source = auto_assign。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.assigned",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 16,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"source": "auto_assign",
"previous_driver_id": null
}
}
订单失去了司机(交接、撤销、拒绝、超时)。data.previous_driver_id 表示此前是谁持有。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.unassigned",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 16,
"route_id": 5120,
"driver_id": null,
"driver_name": "Alex",
"customer_id": 77,
"previous_driver_id": 42,
"source": "driver_duty_release"
}
}
司机在 App 中接受了自动分配的订单(需要接单的司机上下班模式)。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.accepted",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 16,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"reason": null
}
}
司机拒绝了已分配的订单;data.reason 携带可选的自由文本原因。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.rejected",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 16,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"reason": "Too far away"
}
}
司机已开始取件(状态 开始取货 / 取货中)。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.pickup_started",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 45,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"previous_status_id": 16
}
}
包裹已被取件(状态 已取件)。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.picked_up",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 7,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"previous_status_id": 45
}
}
包裹正在送往收件人途中(状态 开始送货 / 配送中)。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.on_the_way",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 46,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"previous_status_id": 7
}
}
配送成功(状态 递送成功)。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.completed",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 8,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"previous_status_id": 46
}
}
配送尝试失败(稍后重派、需重新安排、收件人拒收)。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.failed",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 9,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"previous_status_id": 46
}
}
订单已取消。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.cancelled",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 12,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"previous_status_id": 16
}
}
员工(或在允许的情况下由司机)将订单标记为待取件(调度选项 → 待取件)。
载荷示例
{
"event_id": "OLE-01JABCDE",
"event_type": "order.ready",
"livemode": true,
"occurred_at": "2026-08-17T10:00:05-04:00",
"data": {
"order_id": 88231,
"order_ref": "SO-100234",
"tracking_number": "TRK-1001",
"type": "D",
"status_id": 16,
"route_id": 5120,
"driver_id": 42,
"driver_name": "Alex",
"customer_id": 77,
"ready_by_user_id": 3
}
}
司机在 App 中上班或下班(司机上下班选项)。
载荷示例
{
"event_id": "OLE-01JABCDF",
"event_type": "driver.on_duty_changed",
"livemode": true,
"occurred_at": "2026-08-17T08:00:00-04:00",
"data": {
"driver_id": 42,
"driver_name": "Alex",
"on_duty": true,
"changed_at": "2026-08-17T08:00:00-04:00"
}
}
来自 App 或追踪器的司机位置,按司机通过 driver_location_min_interval_sec 节流(默认 60 秒)。仅发送到 driver_location_webhook_url。
载荷示例
{
"event_id": "OLE-01JABCDG",
"event_type": "driver.location_update",
"livemode": true,
"occurred_at": "2026-08-17T10:00:30-04:00",
"data": {
"driver_id": 42,
"driver_name": "Alex",
"latitude": 45.5017,
"longitude": -73.5673,
"recorded_at": "2026-08-17T10:00:30-04:00",
"route_id": 5120,
"heading": 90,
"speed": 12.5
}
}
如何配置: 设置 → Webhook(或 GET/PUT /api/v1/webhook-settings、GraphQL webhookSettingsUpdate):order_lifecycle_webhook_url 接收所有 order.* 事件和 driver.on_duty_changed;order_lifecycle_events 可将其收窄为逗号分隔的列表;driver_location_webhook_url 和 driver_location_min_interval_sec 控制 driver.location_update。多个 URL 可用逗号分隔。投递记录会以 reference_type order / driver 显示在 Webhook 投递日志中。
签名与校验: 签名方式与您账户的其他所有出站 Webhook 完全一致:传统的 Signature 请求头,加上使用您的 webhook_sign_secret 生成的 X-Webhook-Id / X-Webhook-Timestamp / X-Webhook-Signature-V2。重试会复用同一个 event_id——请据此去重。
设备订单事件
针对由您的智能柜、自助终端和智能投递箱处理的包裹的可选事件:包裹存入设备、被取走、被工作人员取出或超过取件期限,以及该包裹上异常的创建与解决。纯新增——现有 Webhook 均不变,配置 device_order_webhook_url 之前不会发送任何内容。
包裹已放入设备,正在等待下一位处理人(收件人、快递员或运营人员,见 data.device_order.next_actor)。due_at 为取件截止时间。
载荷示例
{
"event_id": "DOE-90017",
"event_type": "device_order.stored",
"livemode": true,
"occurred_at": "2026-09-15T10:06:00-04:00",
"data": {
"device_order": {
"id": 5012,
"kind": "shipout_pickup",
"status": "stored",
"next_actor": "recipient",
"device_type": "smart_locker",
"device_id": 11,
"device_name": "LOCKER-A",
"grid_code": "A-12",
"reference_number": "SR100234",
"order_id": 88231,
"external_order_id": null,
"due_at": "2026-09-18T10:06:00-04:00",
"overdue_at": null,
"stored_at": "2026-09-15T10:06:00-04:00",
"ended_at": null,
"removal_reason": null
}
}
}
包裹已被其等待的人取走——收件人、快递员,或清空智能投递箱的工作人员。
载荷示例
{
"event_id": "DOE-90017",
"event_type": "device_order.collected",
"livemode": true,
"occurred_at": "2026-09-15T10:06:00-04:00",
"data": {
"device_order": {
"id": 5012,
"kind": "shipout_pickup",
"status": "collected",
"next_actor": "nobody",
"device_type": "smart_locker",
"device_id": 11,
"device_name": "LOCKER-A",
"grid_code": "A-12",
"reference_number": "SR100234",
"order_id": 88231,
"external_order_id": null,
"due_at": "2026-09-18T10:06:00-04:00",
"overdue_at": null,
"stored_at": "2026-09-15T10:06:00-04:00",
"ended_at": "2026-09-16T18:40:00-04:00",
"removal_reason": null
}
}
}
工作人员将包裹从设备中取出。removal_reason 说明原因:overdue_return、handover、relay、anomaly 或 recovery。
载荷示例
{
"event_id": "DOE-90017",
"event_type": "device_order.removed",
"livemode": true,
"occurred_at": "2026-09-15T10:06:00-04:00",
"data": {
"device_order": {
"id": 5012,
"kind": "shipout_pickup",
"status": "removed",
"next_actor": "nobody",
"device_type": "smart_locker",
"device_id": 11,
"device_name": "LOCKER-A",
"grid_code": "A-12",
"reference_number": "SR100234",
"order_id": 88231,
"external_order_id": null,
"due_at": "2026-09-18T10:06:00-04:00",
"overdue_at": "2026-09-18T10:15:00-04:00",
"stored_at": "2026-09-15T10:06:00-04:00",
"ended_at": "2026-09-19T09:00:00-04:00",
"removal_reason": "overdue_return"
}
}
}
包裹超过 due_at 仍未被取走。包裹仍在设备中,取件码依然有效;overdue_at 会被设置,next_actor 变为 operator。
载荷示例
{
"event_id": "DOE-90017",
"event_type": "device_order.overdue",
"livemode": true,
"occurred_at": "2026-09-15T10:06:00-04:00",
"data": {
"device_order": {
"id": 5012,
"kind": "shipout_pickup",
"status": "stored",
"next_actor": "operator",
"device_type": "smart_locker",
"device_id": 11,
"device_name": "LOCKER-A",
"grid_code": "A-12",
"reference_number": "SR100234",
"order_id": 88231,
"external_order_id": null,
"due_at": "2026-09-18T10:06:00-04:00",
"overdue_at": "2026-09-18T10:15:00-04:00",
"stored_at": "2026-09-15T10:06:00-04:00",
"ended_at": null,
"removal_reason": null
}
}
}
该包裹的处理上创建了一个异常(例如 door_left_open、deposit_unverified、item_missing、overdue)。data.exception 包含 id、type、severity 和 status。
载荷示例
{
"event_id": "DOE-90017",
"event_type": "device_order.exception_opened",
"livemode": true,
"occurred_at": "2026-09-15T10:06:00-04:00",
"data": {
"device_order": {
"id": 5012,
"kind": "shipout_pickup",
"status": "stored",
"next_actor": "recipient",
"device_type": "smart_locker",
"device_id": 11,
"device_name": "LOCKER-A",
"grid_code": "A-12",
"reference_number": "SR100234",
"order_id": 88231,
"external_order_id": null,
"due_at": "2026-09-18T10:06:00-04:00",
"overdue_at": null,
"stored_at": "2026-09-15T10:06:00-04:00",
"ended_at": null,
"removal_reason": null
},
"exception": {
"id": 311,
"type": "door_left_open",
"severity": "attention",
"status": "open",
"resolution_action": null
}
}
}
有人关闭了该包裹处理上的异常。data.exception.status 为 resolved 或 dismissed,resolution_action 说明所做的处理。
载荷示例
{
"event_id": "DOE-90017",
"event_type": "device_order.exception_resolved",
"livemode": true,
"occurred_at": "2026-09-15T10:06:00-04:00",
"data": {
"device_order": {
"id": 5012,
"kind": "shipout_pickup",
"status": "stored",
"next_actor": "recipient",
"device_type": "smart_locker",
"device_id": 11,
"device_name": "LOCKER-A",
"grid_code": "A-12",
"reference_number": "SR100234",
"order_id": 88231,
"external_order_id": null,
"due_at": "2026-09-18T10:06:00-04:00",
"overdue_at": null,
"stored_at": "2026-09-15T10:06:00-04:00",
"ended_at": null,
"removal_reason": null
},
"exception": {
"id": 311,
"type": "door_left_open",
"severity": "attention",
"status": "resolved",
"resolution_action": "marked_ok"
}
}
}
载荷示例: data.device_order:id, kind, status, next_actor, device_type, device_id, device_name, grid_code, reference_number, order_id, external_order_id, due_at, overdue_at, stored_at, ended_at, removal_reason(时间为 ISO 8601 格式,尚未发生时为 null)。异常事件另含 data.exception:id、type、severity、status、resolution_action。取件码永远不会包含在内。event_id 为 DOE-<账本事件 id>,重试时保持不变。
如何配置: 设置 → Webhooks(或 GET/PUT /api/v1/webhook-settings):device_order_webhook_url 接收所有 device_order.* 事件;device_order_events 可将其限定为逗号分隔的列表。多个 URL 可用逗号分隔。投递记录显示在 Webhook 投递日志中,reference_type 为 device_order。
签名与校验: 签名方式与您账户的其他所有出站 Webhook 完全一致:传统的 Signature 请求头,加上使用您的 webhook_sign_secret 生成的 X-Webhook-Id / X-Webhook-Timestamp / X-Webhook-Signature-V2。重试会复用同一个 event_id——请据此去重。
合作承运商以其自有账户投递到您柜中的包裹不会通过此通道发送;合作方通过其供应商智能柜 Webhook 接收这些事件。
如何配置
可在两个层级配置:商户级(覆盖全部)或按客户级(针对特定 B2B 子账号覆盖)。
1. 进入设置
登录后进入「设置 → API 与 Webhook」。客户级覆盖在客户详情页。
2. 设置签名密钥
选一个至少 16 字符的字符串,建议使用 32 字节以上随机串。接收端会用它来校验签名。
3. 填您需要的事件 URL
只填您关心的事件的 URL,其余留空即可跳过。
webhook_sign_secret您在设置页配置一个共享密钥,每条出站 webhook 都用它签名。接收端用相同密钥重新计算签名并对比 — 一致即代表内容真实未被篡改。
order_create_webhook_url本地配送订单(送货 D / 取件 P / 点对点 P2P)创建时触发,覆盖所有入口:网页表单、REST/GraphQL API、电商平台同步、自动规则、导入行等。Label-service 及其他非配送类型订单不触发。如果同一收件方同时配置了 order_create_async_postback_url,批量导入流程中将跳过此 webhook(由批量回调统一通知)。对应字段 order_create_webhook_url。
order_status_change_webhook_url订单状态每次变更时触发 — 取件、运输中、已送达、异常、取消等。对应字段 order_status_change_webhook_url。
track_event_webhook_url包裹生命周期事件触发时(提交信息、开始配送、配送成功、未送达等)。对应字段 tracking_event_webhook_url。「已送达」与「已取件」事件还会附带签收凭证:proof_files 以及 proof_files_detail(file_id、type、url、full_url、带签名的下载地址)。事件之后才上传的照片通过 pod.files_updated 推送。每个文件还附带对应事件信息:tracking_event_id、tracking_event_status_id、tracking_event_key、service_type(1 = delivery / 2 = pickup)和 service_status(1 = success / 2 = failed);未记录事件的历史文件这些字段为 null。
order_create_async_postback_url批量订单导入处理完成后触发一次,载荷包含每行的处理结果数组。对应字段 order_create_async_postback_url。
pod_files_webhook_url签收照片或签名被新增、替换或删除时触发(action:added / updated / removed)——每个文件一次推送,无需再轮询附件。配置 pod_files_webhook_url 后启用。每个文件还附带对应事件信息:tracking_event_id、tracking_event_status_id、tracking_event_key、service_type(1 = delivery / 2 = pickup)和 service_status(1 = success / 2 = failed);未记录事件的历史文件这些字段为 null。
order_deleted_webhook_url订单被永久删除时触发,便于你的系统同步移除。配置 order_deleted_webhook_url 后启用。
order_cancel_failed_webhook_url取消请求被拒绝时触发(例如订单已在派送中),便于你的运营管道监控失败的取消,无需轮询 API 响应。配置 order_cancel_failed_webhook_url 后启用。
route_board_webhook_url线路看板席位易主或看板状态变化时触发——action 字段说明发生了什么(claimed、standby、pooled、promoted、withdrawn、vetoed、replaced、assigned、awarded、lost、displaced、settled、board_opened、board_closed、board_cancelled)。仅限企业级配置。通过 route_board_webhook_url 订阅。
device_order_webhook_url针对由您的智能柜、自助终端和智能投递箱处理的包裹的可选事件:包裹存入设备、被取走、被工作人员取出或超过取件期限,以及该包裹上异常的创建与解决。纯新增——现有 Webhook 均不变,配置 device_order_webhook_url 之前不会发送任何内容。
集成更新指南
API 与 Webhook 的全部新能力——幂等取消、对账拉取、v2 签名、新事件——附可直接复制的示例,全部向前兼容。
签名与校验
每条出站 webhook 在请求头中携带 HMAC-SHA256(十六进制)签名。接收端用共享密钥对原始请求体重新计算签名并对比,不匹配则拒绝请求。
校验步骤
在任何解析或中间件改动之前,读取原始请求体。
使用 hash_hmac('sha256', rawBody, sharedSecret) 计算签名,并以十六进制输出。
与 Signature 头做恒定时间比较(PHP 用 hash_equals,Node 用 crypto.timingSafeEqual)。
签名匹配才返回 2xx,否则返回 401。
PHP
Node.js
Python
Go
红宝石
<?php
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $rawBody, $sharedSecret);
if (!hash_equals($expected, $received)) {
http_response_code(401);
exit('Bad signature');
}
$payload = json_decode($rawBody, true);
// ... handle event ...
http_response_code(200);
echo 'ok';
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use('/webhooks/superroute', express.raw({ type: 'application/json' }), (req, res) => {
const received = req.header('Signature') || '';
const expected = crypto.createHmac('sha256', sharedSecret)
.update(req.body).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))) {
return res.status(401).send('Bad signature');
}
const payload = JSON.parse(req.body.toString());
// ... handle event ...
res.status(200).send('ok');
});
import hmac, hashlib
from flask import Flask, request, abort
app = Flask(__name__)
@app.route('/webhooks/superroute', methods=['POST'])
def webhook():
received = request.headers.get('Signature', '')
expected = hmac.new(shared_secret.encode(), request.data, hashlib.sha256).hexdigest()
if not hmac.compare_digest(received, expected):
abort(401)
payload = request.get_json()
# ... handle event ...
return 'ok', 200
func handleWebhook(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
received := r.Header.Get("Signature")
mac := hmac.New(sha256.New, []byte(sharedSecret))
mac.Write(body)
expected := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(received), []byte(expected)) {
w.WriteHeader(401); return
}
// ... handle event ...
w.WriteHeader(200)
w.Write([]byte("ok"))
}
require 'openssl'
require 'rack/utils'
post '/webhooks/superroute' do
raw = request.body.read
received = request.env['HTTP_SIGNATURE'] || ''
expected = OpenSSL::HMAC.hexdigest('sha256', shared_secret, raw)
halt 401 unless Rack::Utils.secure_compare(received, expected)
payload = JSON.parse(raw)
# ... handle event ...
status 200
'ok'
end
重试与可靠性
您的接收端应尽快返回 2xx。若返回其它状态、超时或不可达,我们会重试投递。
退避策略
指数退避 — 约 10s、100s、1000s、10000s
务必实现幂等. 同一事件可能被重试多次,您可能会收到多次。请用订单号 / 跟踪号作为去重键,已处理事件 ID 至少缓存 24 小时。
推荐响应方式. 快速返回 HTTP 200,再异步处理实际业务。不要在 webhook 处理函数里做慢操作 — 会撞到 3 秒超时。
签名校验工具
粘贴您收到的载荷、Signature 头值和您的密钥 — 工具会在浏览器本地重新计算签名(数据不会离开本页面)并告诉您是否一致。
发送测试 Webhook
从我们服务器真实发起一次带签名的 webhook 到您指定的 URL。用来验证接收端是否可达、载荷是否能解析、签名校验逻辑是否正确。
HTTP 状态码 / 响应耗时
已发送签名
响应体
最近的 Webhook 投递记录
查看您账户最近的 webhook 投递尝试 — 包含真实生产事件和本页面发的测试。粘贴 Bearer Token 加载。
Bearer Token
事件类型
全部
order.created
order.status_change
tracking.event
order.create_async
pod.files_updated
order.deleted
order.cancel_failed
状态
全部
仅成功
仅失败
加载日志
时间
事件
URL
状态
HTTP
尝试
耗时(ms)
测试?
操作
暂无 webhook 投递记录。
最佳实践
快速返回 HTTP 200,把业务处理放到异步任务,避免触发 3 秒超时。
使用前务必先校验签名。
按"至少一次"语义处理事件 — 按订单号 / 跟踪号去重。
使用 HTTPS 接收端点和有效证书。
记录请求日志,便于发现处理逻辑 bug 后回放。