公共约定
认证、限流、请求格式、幂等性、Webhook 签名和错误码的公共规范。
认证
所有接口需通过 X-API-Key 请求头传递 API Key。
请求头bash
curl -X GET https://api.sp.airgoe.com/api/v2/client/profile \-H "X-API-Key: sk-your-api-key-here"
| 场景 | HTTP 状态码 | 错误码 |
|---|---|---|
| 缺少 API Key | 401 | MISSING_API_KEY |
| API Key 无效或已停用 | 401 | INVALID_API_KEY |
限流
按接入方独立限流,采用滑动窗口算法。
| 接口类型 | 默认限流 | 窗口 |
|---|---|---|
| 履约能力查询 | 60 次/分钟 | 滑动窗口 |
| 创建/取消订单 | 30 次/分钟 | 滑动窗口 |
| 查询类接口 | 120 次/分钟 | 滑动窗口 |
超限处理
超限时服务端返回 HTTP 429,响应头含
Retry-After 表示需等待的秒数。平台级接入方可申请提升配额。请求/响应格式
所有接口统一使用的标准格式。
| 项目 | 值 |
|---|---|
| Content-Type | application/json |
| 编码 | UTF-8 |
| 时间格式 | ISO 8601(2026-03-10T10:30:00.000Z) |
| 坐标系 | WGS-84(经纬度) |
成功响应
标准成功json
{"success": true,"data": { ... }}
分页成功json
{"success": true,"data": [ ... ],"meta": {"total": 100,"page": 1,"pageSize": 20,"totalPages": 5}}
错误响应
标准错误json
{"success": false,"error": "ERROR_CODE","message": "可读的错误描述"}
带详情的错误json
{"success": false,"error": "VALIDATION_ERROR","message": "参数校验失败","details": {"field": "cargo.weight","reason": "weight 必须为正数"}}
幂等性
写入接口支持通过 X-Idempotency-Key 请求头实现幂等。
幂等请求bash
curl -X POST https://api.sp.airgoe.com/api/v2/orders \-H "X-API-Key: sk-your-api-key" \-H "X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \-H "Content-Type: application/json" \-d '{ "quotationId": "QT20260310100001" }'
| 属性 | 值 |
|---|---|
| 格式 | UUID v4(推荐) |
| 有效期 | 24 小时;过期后,相同 Key 视为新请求 |
| 冲突 | 相同 Key 但请求体不同返回 409 IDEMPOTENCY_CONFLICT |
Webhook 签名验证
SP 对所有 Webhook 推送使用 HMAC-SHA256 签名。务必验证签名以防止伪造请求。
Webhook 请求头
| 请求头 | 说明 |
|---|---|
X-Webhook-Signature | HMAC-SHA256 签名 |
X-Webhook-Timestamp | Unix 时间戳(毫秒) |
X-Webhook-Id | 唯一事件 ID,用于幂等处理 |
验证流程
1
从请求头中提取 X-Webhook-Timestamp 和 X-Webhook-Signature
2
拼接:timestamp + "." + rawRequestBody
3
使用您的 webhook_secret 作为密钥计算 HMAC-SHA256
4
将计算结果与请求头中的签名进行比对
5
(可选)验证时间戳在 5 分钟以内,防止重放攻击
Node.js 验证示例javascript
const crypto = require('crypto');function verifyWebhookSignature(secret, timestamp, body, signature) {const payload = timestamp + '.' + body;const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(payload).digest('hex');// 常量时间比较,防止时序攻击return crypto.timingSafeEqual(Buffer.from(expected),Buffer.from(signature));}// 在 Webhook 处理函数中:app.post('/webhooks/sp', (req, res) => {const timestamp = req.headers['x-webhook-timestamp'];const signature = req.headers['x-webhook-signature'];const body = JSON.stringify(req.body);if (!verifyWebhookSignature(WEBHOOK_SECRET, timestamp, body, signature)) {return res.status(401).send('Invalid signature');}// 处理 Webhook 事件...const { eventType, orderId, data } = req.body;res.status(200).send('OK');});
重试策略
| 属性 | 值 |
|---|---|
| 超时时间 | 5 秒 |
| 最大重试次数 | 3 次 |
| 重试间隔 | 指数退避:10 秒、30 秒、90 秒 |
| 成功标准 | HTTP 2xx 响应 |
错误码参考
按 HTTP 状态码分类的完整错误码列表。
认证错误(HTTP 401)
| 错误码 | 说明 |
|---|---|
MISSING_API_KEY | 缺少 X-API-Key 请求头 |
INVALID_API_KEY | API Key 无效或已停用 |
校验错误(HTTP 400)
| 错误码 | 说明 |
|---|---|
VALIDATION_ERROR | 通用校验失败,details 字段包含具体信息 |
CARGO_OVERWEIGHT | 货物重量超出机队最大载荷 |
CARGO_OVERSIZE | 货物尺寸超出机队最大货舱 |
QUOTATION_EXPIRED | 报价单已过期 |
ORDER_CANNOT_CANCEL | 当前订单状态不允许取消 |
未找到(HTTP 404)
| 错误码 | 说明 |
|---|---|
ORDER_NOT_FOUND | 订单不存在 |
STATION_NOT_FOUND | 站点不存在 |
冲突(HTTP 409)
| 错误码 | 说明 |
|---|---|
IDEMPOTENCY_CONFLICT | 幂等键已存在但请求体不同 |
QUOTATION_USED | 报价单已被其他订单使用 |
业务不可用(HTTP 422)
| 错误码 | 说明 |
|---|---|
NO_FULFILLMENT_AVAILABLE | 全部 7 层评估均不通过,无可用航线 |
服务不可用(HTTP 503)
| 错误码 | 说明 |
|---|---|
SERVICE_UNAVAILABLE | 下游平台(OP/BAS/RP)不可用 |