WEBHOOKS

网络钩子

实时接收 Superroute 事件 — 订单、状态变更、跟踪更新。带签名校验、自动重试和内置调试工具。

Webhook 集成指南
开发者中心 首页

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。

载荷示例
订单.status_change

订单状态每次变更时触发 — 取件、运输中、已送达、异常、取消等。对应字段 order_status_change_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

批量订单导入处理完成后触发一次,载荷包含每行的处理结果数组。对应字段 order_create_async_postback_url。

载荷示例
POD 附件变更

签收照片或签名被新增、替换或删除时触发(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 后启用。

载荷示例
订单取消失败

取消请求被拒绝时触发(例如订单已在派送中),便于你的运营管道监控失败的取消,无需轮询 API 响应。配置 order_cancel_failed_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 订阅。

载荷示例

供应商智能柜事件

面向对接智能柜的第三方配送供应商的独立 webhook 通道。事件投递到你供应商账号配置的端点,每个端点可订阅任意事件子集。

partner_locker.delivery.doors_opened

已开门 — 投柜尝试的柜门打开瞬间触发——无论司机是在柜机屏幕上输入送件码开门,还是通过远程开门 API 开门——换格重开也会触发。opening 数据块列出每个已打开格口的 grid_id、硬件门板号 compartment_number 和自提柜格口号 pickup_locker_number(按列从上到下、再从左到右的显示序号)。每个格口还带 pickup_locker_code,即收件人要去的「{货架编码}-{格口号}」标签;格口没有格口号时为 null。负载中还包含 pickup_code——收件人取件码,开门瞬间即已分配;司机确认放入后仍是同一个码,但只有在确认放入之后才能用于取件。

载荷示例
partner_locker.delivery.delivered

已投柜 — 投柜确认、包裹放入柜中后触发。载荷中包含收件人取件码。每个格口还带 pickup_locker_code,即收件人要去的「{货架编码}-{格口号}」标签;格口没有格口号时为 null。通过远程开门 API 打开的柜门,柜机上报全部已开格口关门后由平台自动结算,因此无需调用 confirm 也会触发本事件;confirmed_by 标明结算路径:courier_terminal、partner_api、door_close、timeout_door_closed 或 console。

载荷示例
partner_locker.pickup.completed

已取件 — 收件人取走已寄存包裹后触发。

载荷示例
partner_locker.delivery.failed

投递失败 — 投递失败时触发;包含每个包裹的失败代码。

载荷示例
partner_locker.delivery.expired

已过期 — 未使用的送件码或未取件的寄存超过有效期时触发。

载荷示例
partner_locker.delivery.cancelled

已取消 — 投递在完成前被取消时触发。

载荷示例
partner_locker.delivery.correction_reopened

修正重开门 — 在纠错窗口内重新打开已寄存格口以修正放错位置时触发——可在柜机屏幕或通过 API 操作。correction 块中列出被重开的格口。每个格口还带 pickup_locker_code,即收件人要去的「{货架编码}-{格口号}」标签;格口没有格口号时为 null。

载荷示例
partner_locker.delivery.code_rotated

partner_locker_delivery.event_type_code_rotated — 合作伙伴更换投递的送件码或取件码时触发。rotation 数据块说明换的是哪种码、何时更换、是否重发了收件人通知——新码本身绝不通过 webhook 传递,只在换码 API 的直接响应中揭示一次。

载荷示例

签名与校验: 供应商智能柜 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。覆盖配送任务的整个生命周期,快递商无需再轮询新任务。该类别与下方的智能柜事件相互独立:每个服务商按类别分别配置独立的端点、签名密钥和事件订阅——可在其自己的服务商门户中配置,也可由平台运营方配置。

delivery.assignment.created

任务已创建 — 当订单被分配给服务商时触发——无论是通过自动规则还是手动分配。载荷包含任务编号、订单标识和包裹跟踪号。

载荷示例
delivery.assignment.handed_over

包裹已移交 — 当仓库将该任务的全部包裹实际移交给服务商时触发。

载荷示例
delivery.assignment.cancelled

任务已取消 — 当平台从服务商处撤回任务时触发。reason 字段区分 cancelled(任务在承运商处被取消)、fallback_to_self_delivery(平台将订单收回自行配送)和 reassigned(订单已改派给其他服务商)。

载荷示例
delivery.assignment.partial_delivered

部分送达 — 当一票货件中部分包裹已送达、其余仍在途时触发。packages 数组给出每个包裹的结局,legs 列出在承运商处下的第三方订单——承运商不支持一票多件时,每个包裹一个订单。

载荷示例

签名与校验: 第三方配送 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 之前不会发送任何内容。

order.assigned

订单已分配给司机(手动、线路规划或自动分配)。由编排器分配时 data.source = auto_assign。

载荷示例
order.unassigned

订单失去了司机(交接、撤销、拒绝、超时)。data.previous_driver_id 表示此前是谁持有。

载荷示例
order.accepted

司机在 App 中接受了自动分配的订单(需要接单的司机上下班模式)。

载荷示例
order.rejected

司机拒绝了已分配的订单;data.reason 携带可选的自由文本原因。

载荷示例
order.pickup_started

司机已开始取件(状态 开始取货 / 取货中)。

载荷示例
order.picked_up

包裹已被取件(状态 已取件)。

载荷示例
order.on_the_way

包裹正在送往收件人途中(状态 开始送货 / 配送中)。

载荷示例
order.completed

配送成功(状态 递送成功)。

载荷示例
order.failed

配送尝试失败(稍后重派、需重新安排、收件人拒收)。

载荷示例
order.cancelled

订单已取消。

载荷示例
order.ready

员工(或在允许的情况下由司机)将订单标记为待取件(调度选项 → 待取件)。

载荷示例
driver.on_duty_changed

司机在 App 中上班或下班(司机上下班选项)。

载荷示例
driver.location_update

来自 App 或追踪器的司机位置,按司机通过 driver_location_min_interval_sec 节流(默认 60 秒)。仅发送到 driver_location_webhook_url。

载荷示例

如何配置: 设置 → 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 之前不会发送任何内容。

device_order.stored

包裹已放入设备,正在等待下一位处理人(收件人、快递员或运营人员,见 data.device_order.next_actor)。due_at 为取件截止时间。

载荷示例
device_order.collected

包裹已被其等待的人取走——收件人、快递员,或清空智能投递箱的工作人员。

载荷示例
device_order.removed

工作人员将包裹从设备中取出。removal_reason 说明原因:overdue_return、handover、relay、anomaly 或 recovery。

载荷示例
device_order.overdue

包裹超过 due_at 仍未被取走。包裹仍在设备中,取件码依然有效;overdue_at 会被设置,next_actor 变为 operator。

载荷示例
device_order.exception_opened

该包裹的处理上创建了一个异常(例如 door_left_open、deposit_unverified、item_missing、overdue)。data.exception 包含 id、type、severity 和 status。

载荷示例
device_order.exception_resolved

有人关闭了该包裹处理上的异常。data.exception.status 为 resolved 或 dismissed,resolution_action 说明所做的处理。

载荷示例

载荷示例: 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(十六进制)签名。接收端用共享密钥对原始请求体重新计算签名并对比,不匹配则拒绝请求。

算法
HMAC-SHA256 (hex)
头名称
Signature
校验步骤
  1. 在任何解析或中间件改动之前,读取原始请求体。
  2. 使用 hash_hmac('sha256', rawBody, sharedSecret) 计算签名,并以十六进制输出。
  3. 与 Signature 头做恒定时间比较(PHP 用 hash_equals,Node 用 crypto.timingSafeEqual)。
  4. 签名匹配才返回 2xx,否则返回 401。
<?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。若返回其它状态、超时或不可达,我们会重试投递。

最大尝试
5 次(首次 + 4 次重试)
单次超时
3 秒
退避策略
指数退避 — 约 10s、100s、1000s、10000s
务必实现幂等. 同一事件可能被重试多次,您可能会收到多次。请用订单号 / 跟踪号作为去重键,已处理事件 ID 至少缓存 24 小时。
推荐响应方式. 快速返回 HTTP 200,再异步处理实际业务。不要在 webhook 处理函数里做慢操作 — 会撞到 3 秒超时。

签名校验工具

粘贴您收到的载荷、Signature 头值和您的密钥 — 工具会在浏览器本地重新计算签名(数据不会离开本页面)并告诉您是否一致。

发送测试 Webhook

从我们服务器真实发起一次带签名的 webhook 到您指定的 URL。用来验证接收端是否可达、载荷是否能解析、签名校验逻辑是否正确。

最近的 Webhook 投递记录

查看您账户最近的 webhook 投递尝试 — 包含真实生产事件和本页面发的测试。粘贴 Bearer Token 加载。

时间 事件 URL 状态 HTTP 尝试 耗时(ms) 测试? 操作
暂无 webhook 投递记录。

最佳实践