You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

982 lines
35 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 美美与共查价系统 · 宿主 OpenAPI 技术对接文档
**版本**1.2
**更新日期**2026-07-22
**适用对象**:第三方宿主系统(服务端对接 / 网页嵌入)
---
## 1. 文档说明
本文档供第三方宿主系统对接「美美与共查价中台」使用。
中台对接 **两个官网**,每个官网各有 **免账号 / 登录后** 两种查价方式,合计 **四条互斥路线**
| 路线 ID | 官网 | 账密 | 说明 |
|---------|------|------|------|
| `MS_GUEST` | MotherShip | 无 | 免登录 Widget / Direct 路径 |
| `MS_LOGGED_IN` | MotherShip | 有(该客户已绑定 MotherShip 账密) | 官网 Create shipment 登录态路径 |
| `FLOCK_GUEST` | Flock Freight | 无 | 免登录 Direct失败可回退 DOM |
| `FLOCK_LOGGED_IN` | Flock Freight | 有(该客户已绑定 Flock 账密) | 强制官网登录 DOM禁止 Direct |
**核心规则**:根据「用户/宿主上传的承运商账密」与「本票货物/地址参数是否符合某官网硬限」判定路线后,**整票只走一条路线**,禁止混用上限、混用接口、混用结果字段。
本文档仅列出对调用方有意义且可依赖的字段。响应中如出现未在本文档定义的其他字段,均属于平台保留字段,调用方不应依赖。
**对接形态**
| 形态 | 说明 | 见章节 |
|------|------|--------|
| OpenAPI 服务端调用 | 宿主后端带 API Key 调两步查价接口 | §2§6 |
| 网页嵌入iframe | 宿主页面嵌查价 UI**须直传类型+Key跳过登录页** | §2.5 |
> **安全要求**API Key、承运商官网账密仅允许保存在宿主服务端或经我方管理端加密存储禁止写入宿主公开前端仓库、移动端安装包。OpenAPI 调用禁止把 Key 放进 URLiframe 嵌入仅允许由**宿主服务端**在生成 `iframe.src` 时短时拼入查询串(见 §2.5)。
---
## 2. 接入信息
### 2.1 基础地址
| 环境 | Base URL |
|------|----------|
| 当前联调 / 演示 | `https://if.dev.51track.vip` |
> 公网访问**不要**追加端口(如 `:30325`)。`:30325` 仅用于服务器本机调试。
嵌入页路径:`{Base URL}/embed-demo`
### 2.2 请求格式
- 请求方法:以各接口定义为准
- 请求体JSON
- 字符编码UTF-8
- Content-Type`application/json`
### 2.3 鉴权请求头OpenAPI
| 请求头 | 必填 | 说明 |
|--------|------|------|
| Authorization | 是 | `Bearer <API Key>`Key 通常以 `chj_` 开头,由我方管理端发放 |
| X-Customer-Id | 是 | 客户 ID必须与 API Key 绑定一致 |
| Content-Type | 是 | 固定为 `application/json` |
本次联调凭证(示例,正式值由我方单独发放):
| 配置项 | 值 | 说明 |
|--------|-----|------|
| API Key | `<由我方发放>` | 放入 `Authorization: Bearer ...` |
| Customer ID | `<由我方发放>` | 放入 `X-Customer-Id` |
> **注意OpenAPI**API Key 不得放入 URL、请求体或业务日志明文。
**请求头示例**
```http
Authorization: Bearer chj_xxxxxxxxxxxxxxxxxxxxx
X-Customer-Id: CUST_001
Content-Type: application/json
```
内网联调若使用环境 Token非正式第三方形态
```http
Authorization: Bearer demo-host-token
X-Customer-Id: CUST_001
Content-Type: application/json
```
### 2.4 通用响应结构
| 字段 | 类型 | 说明 |
|------|------|------|
| code | Number / String | `0` 表示接口调用成功;非 `0` 表示鉴权、参数或业务处理失败 |
| message | String | 提示信息;成功时通常为 `ok` |
| data | Object / null | 成功时的业务数据;失败时多为 `null` |
### 2.5 网页嵌入iframe / CC 等宿主)— 跳过登录页
#### 2.5.1 目标
宿主将查价系统嵌进自有页面时:
1. **用户不应再看到**嵌入页「客户登录」表单。
2. 宿主传入 **登录类型 + 凭证** 后,直接进入 **MotherShip / Flock Freight** 两模块门户。
3. 模块内可再选「免账号 / 登录后」;登录后路线需已绑定承运商官网账密(可在嵌入页内配置)。
#### 2.5.2 URL 查询参数
| 参数 | 必填 | 说明 |
|------|------|------|
| `login_type` | 建议 | `api_key``password`;别名:`type`、`lt` |
| `api_key` | Key 登录必填 | 客户 API Key别名`key`、`token` |
| `customer_id` | 密码登录必填 | 客户编号;别名:`customerId` |
| `password` | 密码登录必填 | 嵌入页客户密码;别名:`pwd` |
| `embed` | 可选 | `1` / `true`:宿主托管壳(隐藏「退出登录」);别名:`hosted`、`from_cc`、`from_host` |
仅带 `api_key`(或 `key`)且未写 `login_type` 时,按 **`api_key` 登录**处理。
#### 2.5.3 推荐写法CCAPI Key
```text
https://if.dev.51track.vip/embed-demo?login_type=api_key&api_key=<API_Key>&embed=1
```
短写:
```text
https://if.dev.51track.vip/embed-demo?type=api_key&key=<API_Key>&embed=1
```
**iframe 示例Key 须由宿主后端渲染进 HTML禁止写死在公开前端仓库**
```html
<iframe
title="查价"
src="https://if.dev.51track.vip/embed-demo?login_type=api_key&api_key=chj_xxxxxxxx&embed=1"
style="width:100%;height:900px;border:0;"
allow="clipboard-read; clipboard-write"
></iframe>
```
账号密码直登(较少用):
```text
https://if.dev.51track.vip/embed-demo?login_type=password&customer_id=CUST_001&password=<密码>&embed=1
```
#### 2.5.4 行为与安全
| 步骤 | 行为 |
|------|------|
| 1 | 打开嵌入页 → 读取查询串 → 调用 `POST /api/embed-demo/login` |
| 2 | 成功 → 写入嵌入会话 Cookie → 进入两模块门户(**不展示登录页** |
| 3 | 前端 `history.replaceState` 去掉地址栏中的 Key/密码等敏感参数 |
| 4 | 失败 → 展示错误文案,并可回落手动登录表单 |
安全约定:
- Key **只允许**宿主服务端拼进 `iframe.src`;用户浏览器地址栏会短暂可见,登录成功后会被剥离。
- 生产环境嵌入 Cookie 使用 `SameSite=None; Secure`,以支持跨站 iframe。
- 勿在公开仓库、前端构建产物、CDN 静态配置中明文提交 API Key。
- OpenAPI 仍须走 `Authorization`§2.3),与嵌入 URL 直传互不替代。
#### 2.5.5 常见错误
| 现象 | 可能原因 | 处理 |
|------|----------|------|
| 仍停在登录页 | 未传 `api_key` / `login_type`,或 Key 为空 | 检查 iframe `src` |
| 提示 API Key 无效 | Key 错误、已轮换、或客户停用 | 管理端核对 Key 与客户状态 |
| 提示缺少 Authorization | 宿主在调 OpenAPI 时未带鉴权头 | 与嵌入无关;按 §2.3 补头 |
| 登录成功但刷新掉登录 | 跨站 Cookie 被拦 / 非 HTTPS | 确认生产 HTTPSCookie 为 None+Secure |
**成功响应示例**OpenAPI 通用):
```json
{
"code": 0,
"message": "ok",
"data": { }
}
```
**失败响应示例**
```json
{
"code": "VALIDATION_FAILED",
"message": "请填写 2 位州码",
"data": null
}
```
> 调用方不能仅根据 HTTP 状态码判断业务成功。仅当 `code = 0` 时,才能判定本次接口调用成功。
> 异步询价业务失败时,轮询接口可能仍返回 HTTP 200 且 `code = 0`,此时须检查 `data.status`:若为 `failed`,按失败处理并展示 `data.error_message`。
---
## 3. 路线判定(必读)
宿主在发起询价前,按下列顺序 **确定唯一路线**,然后只调用该路线对应接口与字段。
### 3.1 判定流程
```
1) 宿主明确选择官网,或按参数硬限自动匹配:
- 符合 MotherShip 硬限且业务选 MotherShip → 进入 MS_*
- 符合 Flock 硬限且业务选 Flock → 进入 FLOCK_*
- 两网站都不符合 → 拒绝询价,提示用户改货改地址
- 禁止:同一票并行打两家再拼结果(除非宿主产品明确要求「比价」且各自独立展示)
2) 查该客户在我方是否已绑定对应官网账密(管理端「查价网站账密」):
- MotherShip 已绑账密 → 必须走 MS_LOGGED_IN服务端强制登录态禁止匿名 Direct/缓存价)
- MotherShip 未绑账密 → 走 MS_GUEST
- Flock 已绑账密 → 必须走 FLOCK_LOGGED_IN服务端强制 DOM 登录,禁止 Direct
- Flock 未绑账密 → 走 FLOCK_GUEST
3) 账密与参数必须同属一条路线:
- 有 Flock 账密但货物只符合 MotherShip → 只能走 MS_*Flock 账密本票不用)
- 有 MotherShip 账密但货物只符合 Flock → 只能走 FLOCK_*
- 有账密却按免账号字段凑合提交:服务端仍可能强制登录态,价格口径与免账号不同,宿主须按登录后字段规范传参
```
### 3.2 两官网硬限对照(换算后英制)
| 项 | MotherShipMS_* | FlockFLOCK_* |
|----|--------------------|------------------|
| 地址形态 | 街道 + 城市 + 州码(建议邮编);须经候选确认 | **仅邮编**5 位);提货邮编 ≠ 派送邮编 |
| 托盘 / 件数 | 125 | 免账号 **420**;登录后 `form_mode=logged_in_quick` 时 1999 |
| 单托重量 | 0.019999 lb登录后 Weight each ≤ **5000 lb** | 按**整票总重**,≤ **45000 lb** |
| 尺寸 | L≤999 inW/H≤99 in | L≤636 inW≤102 inH≤108 in |
| 其它 | 整票总重 ≤ 249975 lb | 免账号须满足拖车线性英尺(约 53' |
重量 / 尺寸支持 `lb`/`kg`/`t`(公制吨)、`in`/`cm`/`m` 入参,服务端统一换算为 lb/in小数进一取整。宿主应在提交前按目标官网硬限拦截。
### 3.3 接口总览
| 路线 | 推荐调用链 | 等待方式 | 典型耗时 |
|------|------------|----------|----------|
| `MS_GUEST` | `POST /api/host/quote/candidates``POST /api/host/quote/submit` | **同步**submit 内等待) | 10420 s |
| `MS_LOGGED_IN` | `POST /api/addresses/mothership-suggest`(可选)→ 组地址 → `POST /api/quotes``GET /api/quotes/{quote_id}` | **异步轮询** | 轮询总窗口建议 ≥ 420 s |
| `FLOCK_GUEST` | `POST /api/flock/quotes``GET /api/quotes/{quote_id}` | **异步轮询** | 建议 ≥ 210 s可配置 |
| `FLOCK_LOGGED_IN` | 同上;`flock_input.form_mode = "logged_in_quick"` | **异步轮询** | 建议 ≥ 210 s |
> `MS_GUEST` 也可改用异步三步(`POST /api/quotes` + 轮询),但第三方默认同步对接请用宿主两步接口。
> 已绑定 MotherShip 账密时,即便误调宿主两步接口,服务端仍会强制登录态 RPA宿主两步**不支持**登录后扩展字段(`cargo_lines` / `mothership_details` 等),完整登录后能力请走 `MS_LOGGED_IN` 异步接口。
---
## 4. 承运商账密(路线开关)
账密由我方管理端按客户绑定RPA 运行时解密使用,**不进入询价请求体**。
### 4.1 管理端接口(内部运维,非宿主日常调用)
```http
GET /api/admin/customers/{customer_id}/provider-credentials
PUT /api/admin/customers/{customer_id}/provider-credentials
```
需管理员 JWT。PUT 示例:
```json
{
"provider": "mothership",
"email": "user@example.com",
"password": "********"
}
```
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| provider | String | 是 | `mothership``flock` | 对应官网 |
| email | String | 是 | 非空 | 官网登录邮箱 |
| password | String | 是 | 非空 | 官网登录密码 |
**影响**
- 绑定成功后,该客户对该 `provider` **自动切换到对应 `*_LOGGED_IN` 路线**。
- 账密错误时询价可能返回 `PROVIDER_LOGIN_FAILED`(或业务失败 `error_message` 提示确认账密)。
- 更换账密须按管理端策略操作;勿在业务日志打印明文密码。
宿主侧若自行收集用户官网账密,应通过约定安全通道交给我方写入管理端,或由双方约定的 BFF 代写;**询价 OpenAPI 请求体中不要传官网密码**。
---
## 5. 路线 AMotherShip 免账号(`MS_GUEST`
适用:客户**未**绑定 MotherShip 账密;参数符合 MotherShip 硬限。
### 5.1 调用流程
1. 带鉴权头调用「获取地址候选」。
2. 保存 `host_session_id`(约 30 分钟有效)。
3. 用户从 `pickup_candidates`、`delivery_candidates` 各选 1 条 `selectable = true`
4. 将**完整候选对象**与 `host_session_id` 提交「提交并获取报价」。
5. 第二步可能持续 **10420 秒**;客户端与网关超时建议 **≥ 420 秒**。
6. `status = done` 展示 `quotes[]``failed` 展示中文 `error_message`
> 第二步期间展示 Loading并禁止重复提交。
### 5.2 第一步:获取地址候选
#### 接口地址
`POST /api/host/quote/candidates`
#### 请求参数
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| pickup_address | Object | 是 | 见下表 | 提货草稿地址 |
| delivery_address | Object | 是 | 见下表 | 派送草稿地址 |
| cargo | Object | 是 | 见下表 | 货物信息 |
**地址对象pickup_address / delivery_address**
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| street | String | 是 | 非空 | 街道地址 |
| city | String | 是 | 非空 | 城市 |
| state | String | 是 | 固定 2 位 | 美国州二字码,如 `CA`、`TX` |
| zip | String | 否 | `12345``12345-6789`;可空 | 邮编;建议传 |
**货物对象cargo**
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| weight.value | Number | 是 | 正数 | 单托重量数值 |
| weight.unit | String | 是 | `lb`、`kg` 或 `t`(公制吨);别名 `lbs`/`pound(s)`/`tonne(s)`**禁止** `ton`/`tons`(美制短吨歧义) | 重量单位 |
| dimensions.length / width / height | Number | 是 | 正数 | 单托尺寸 |
| dimensions.unit | String | 是 | `in`、`cm` 或 `m`;别名 `inch`/`meter` 亦可 | 尺寸单位 |
| pallet_count | Number | 是 | 整数 125 | 托盘数 |
| cargo_type | String | 是 | 见 §12.1 | 货物类型枚举 |
**请求示例**
```json
{
"pickup_address": {
"street": "1234 Warehouse Blvd",
"city": "Los Angeles",
"state": "CA",
"zip": "90001"
},
"delivery_address": {
"street": "5678 Distribution Dr",
"city": "Dallas",
"state": "TX",
"zip": "75201"
},
"cargo": {
"weight": { "value": 500, "unit": "kg" },
"dimensions": { "length": 120, "width": 100, "height": 150, "unit": "cm" },
"pallet_count": 2,
"cargo_type": "general_freight"
}
}
```
#### 响应数据
| 字段 | 类型 | 说明 |
|------|------|------|
| data.host_session_id | String (UUID) | 第二步必传;约 30 分钟有效 |
| data.pickup_candidates | Array\<Object\> | 提货候选 |
| data.delivery_candidates | Array\<Object\> | 派送候选 |
| data.requires_selection | Boolean | 是否需要用户选择 |
**候选对象字段**
| 字段 | 类型 | 说明 |
|------|------|------|
| option_id | String | 候选唯一标识 |
| display_label | String | 展示文案 |
| formatted_address | String | 格式化地址 |
| street / city / state / zip | String | 结构化地址 |
| selectable | Boolean | 仅可选 `true` |
| unavailable_reason | String | 不可选原因(可能出现) |
#### 调用后处理规则
1. 必须保存 `host_session_id`
2. 提货、派送各选 1 条 `selectable = true`
3. 第二步必须回传第一步返回的**完整候选对象**,禁止只传 `option_id`,禁止自行拼装。
4. 会话过期后须重新调用本接口。
### 5.3 第二步:提交候选并获取报价
#### 接口地址
`POST /api/host/quote/submit`
#### 请求参数
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| host_session_id | String | 是 | UUID来自第一步 | 宿主会话 ID |
| pickup_candidate | Object | 是 | 第一步完整候选 | 提货地址 |
| delivery_candidate | Object | 是 | 第一步完整候选 | 派送地址 |
#### 超时与重试
- 客户端 HTTP 超时:`420000` ms。
- 网关 `proxy_read_timeout` ≥ 420 秒。
- `SESSION_EXPIRED`:重新走第一步。
- `QUOTE_TIMEOUT`:可稍后重试;建议重新获取候选。
#### 响应数据
| 字段 | 类型 | 说明 |
|------|------|------|
| data.quote_id | String | 报价单号 |
| data.request_id | String | 请求标识 |
| data.status | String | `done` / `failed` |
| data.currency | String | 固定 `USD` |
| data.valid_until | String | ISO 有效期 |
| data.source_type | String | `rpa` / `cache` / `stale` 等 |
| data.quotes | Array\<Object\> | 档位列表 |
| data.error_code / error_message | String | 失败时 |
**quotes[] 字段**
| 字段 | 类型 | 说明 |
|------|------|------|
| service_level | String | 如 `standard`、`guaranteed` |
| rate_option | String | 如 `lowest`、`fastest`、`bestValue` |
| carrier | String | 承运商 |
| transit_days | String | 时效描述 |
| final_total | Number | **对客户展示价USD已含加价** |
> 档位数量不固定,按实际列表展示。
**成功响应示例**
```json
{
"code": 0,
"message": "ok",
"data": {
"quote_id": "QTE_20260630_0003",
"request_id": "xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx",
"status": "done",
"currency": "USD",
"valid_until": "2026-06-30T10:30:00.000Z",
"source_type": "rpa",
"quotes": [
{
"service_level": "standard",
"rate_option": "lowest",
"carrier": "Frontline Freight",
"transit_days": "Est. 5 business days",
"final_total": 337.06
}
]
}
}
```
---
## 6. 路线 BMotherShip 登录后(`MS_LOGGED_IN`
适用:客户**已**绑定 MotherShip 账密;参数符合 MotherShip 硬限(含登录后 Weight each ≤ 5000 lb
服务端检测到账密后会强制登录态查价,禁止命中匿名 Direct / L2 缓存价。
### 6.1 调用流程
1. (推荐)用 `POST /api/addresses/mothership-suggest` 按关键字联想,用户点选候选。
2. 组装完整询价体(含地址确认字段 + 可选登录后字段)。
3. `POST /api/quotes` → 得到 `quote_id``status` 多为 `processing`
4. 轮询 `GET /api/quotes/{quote_id}``done` / `failed`(间隔建议 2 s总窗口 ≥ 420 s
5. 成功时读 `data.quotes[]`(结构同 §5.3)。
### 6.2 地址联想(可选但推荐)
#### 接口地址
`POST /api/addresses/mothership-suggest`
#### 请求参数
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| customer_id | String | 是 | 与鉴权客户一致 | 客户 ID |
| query | String | 是 | 去空白后 ≥ 3 字符 | 用户键入的地址关键字 |
**请求示例**
```json
{
"customer_id": "CUST_001",
"query": "1234 Warehouse"
}
```
#### 响应数据
| 字段 | 类型 | 说明 |
|------|------|------|
| data.candidates | Array\<Object\> | 候选列表(字段同 §5.2 候选对象) |
用户选定后,须把候选映射进 `pickup_address` / `delivery_address`(见下节必填确认字段)。
### 6.3 提交询价
#### 接口地址
`POST /api/quotes`
#### 请求参数(核心)
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| request_id | String | 是 | 非空;建议 UUID | 幂等 / 追踪 |
| customer_id | String | 是 | 与鉴权一致 | 客户 ID |
| pickup_address | Object | 是 | 见下表 | 提货地址(须已确认) |
| delivery_address | Object | 是 | 见下表 | 派送地址(须已确认) |
| weight | Object | 是 | value 正数unit `lb`/`kg`/`t` | 单托重量(兼容字段) |
| dimensions | Object | 是 | unit `in`/`cm`/`m` | 单托尺寸(兼容字段) |
| pallet_count | Number | 是 | 125 | 托盘数;多行时建议为各行 quantity 合计 |
| cargo_type | String | 是 | 见 §12.1 | 货物类型 |
| service_level | String | 否 | `standard` / `guaranteed` | 服务等级 |
| rate_option | String | 否 | `lowest` / `fastest` | 价格偏好 |
| ready_date | String | 否 | `YYYY-MM-DD`;周末会拨到下一工作日 | 可提货日 |
| ready_time | String | 否 | ≤32 字符 | 可提货时间 |
| pickup_accessorials | Array\<String\> | 否 | 每项 ≤64最多 20 | 提货附加服务 id |
| delivery_accessorials | Array\<String\> | 否 | 同上 | 派送附加服务 id |
| cargo_lines | Array\<Object\> | 否 | 120 行 | 登录后多行货物RPA 按行填);**字段已为英制,不做 kg/cm/m/t 换算** |
| mothership_details | Object | 否 | 见下 | 二级 Details联系人/FBA 等) |
**地址对象必填确认字段(与免账号草稿不同)**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| street / city / state | String | 是 | 结构化地址state 2 位 |
| zip | String | 否 | 可空字符串 |
| place_id | String | 是 | 通常用候选 `option_id` |
| formatted_address | String | 是 | 完整展示地址 |
| selected_from_suggestions | Boolean | 是 | 必须为 `true` |
| mothership_option_id | String | 是 | 候选 `option_id` |
| mothership_display_label | String | 是 | 候选 `display_label` |
| selected_from_mothership | Boolean | 是 | 必须为 `true` |
**cargo_lines[]**
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| cargo_type | String | 是 | 行货物类型文案/枚举 |
| quantity | Number | 是 | 正数≤999 |
| weight_lb | Number | 是 | 该行单件重量 **lb**(须已换算;不接受 kg/t建议 ≤5000 |
| length_in / width_in / height_in | Number | 是 | 该行尺寸 **英寸**(须已换算;不接受 cm/m |
**mothership_details节选**
| 字段 | 类型 | 说明 |
|------|------|------|
| pickup / delivery | Object | `company_name`、`suite`、联系人姓名邮箱电话、`reference`、`notes`、`opens_at`、`closes_at` |
| request_delivery_appointment | Boolean | 是否预约送货 |
| fba_number / fba_po_number | String | FBA 相关 |
| cargo[] | Array | `description`、`nmfc`、`hazmat`、`alcohol`、`tobacco`、件数类型与数量等 |
**请求示例(登录后最小可用)**
```json
{
"request_id": "2dd352e6-9a1d-4563-8f13-ebff18aabdb9",
"customer_id": "CUST_001",
"pickup_address": {
"street": "1234 Warehouse Street",
"city": "Los Angeles",
"state": "CA",
"zip": "90001",
"place_id": "Eisx...",
"formatted_address": "1234 Warehouse Street, Los Angeles, CA, USA",
"selected_from_suggestions": true,
"mothership_option_id": "Eisx...",
"mothership_display_label": "1234 Warehouse Street, Los Angeles, CA, USA",
"selected_from_mothership": true
},
"delivery_address": {
"street": "5678 Distribution Dr, Wilmer",
"city": "Dallas",
"state": "TX",
"zip": "75201",
"place_id": "EihE...",
"formatted_address": "Distribution Dr, Wilmer, Dallas, TX, USA",
"selected_from_suggestions": true,
"mothership_option_id": "EihE...",
"mothership_display_label": "Distribution Dr, Wilmer, Dallas, TX, USA",
"selected_from_mothership": true
},
"weight": { "value": 1103, "unit": "lb" },
"dimensions": { "length": 48, "width": 40, "height": 60, "unit": "in" },
"pallet_count": 2,
"cargo_type": "general_freight",
"ready_date": "2026-07-22",
"cargo_lines": [
{
"cargo_type": "general_freight",
"quantity": 2,
"weight_lb": 1103,
"length_in": 48,
"width_in": 40,
"height_in": 60
}
]
}
```
#### 提交成功响应
```json
{
"code": 0,
"message": "ok",
"data": {
"quote_id": "QTE_20260721_0012",
"status": "processing"
}
}
```
随后进入 §9 轮询。
---
## 7. 路线 CFlock 免账号(`FLOCK_GUEST`
适用:客户**未**绑定 Flock 账密;参数符合 Flock 免账号硬限(托盘 420、总重 ≤45000 lb、线性英尺等
服务端优先走 Direct JSON失败时按环境配置可回退 DOM 填表(可能弱注册)。
### 7.1 调用流程
1. 校验邮编、取货日、件数、总重、尺寸与线性英尺。
2. `POST /api/flock/quotes``quote_id` + `processing`
3. 轮询 `GET /api/quotes/{quote_id}`(总窗口建议 ≥ 210 s
4. 成功时读 `data.flock.lines[]`**不是** MotherShip 的 `quotes[]` 形态)。
### 7.2 提交询价
#### 接口地址
`POST /api/flock/quotes`
> 若服务端关闭 Flock 能力,可能返回业务不可用(如 `QUOTE_UNAVAILABLE`)。
#### 请求参数
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| request_id | String | 是 | 非空 | 请求标识 |
| customer_id | String | 是 | 与鉴权一致 | 客户 ID |
| flock_input | Object | 是 | 见下表 | Flock 表单参数 |
**flock_input免账号**
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| pickup_date | String | 是 | `MM/DD/YYYY`;不可早于今天;不可周末 | 提货日 |
| pickup_zip | String | 是 | 5 位邮编 | 提货邮编 |
| delivery_zip | String | 是 | 5 位;≠ pickup_zip | 派送邮编 |
| pallet_count | Number | 是 | 整数 **420** | 托盘件数 |
| total_weight | Object | 是* | value 正数unit `lb`/`kg`/`t` | 整票总重;也可用 `total_weight_lb` |
| dimensions | Object | 否 | 缺省 48×40×48 inunit `in`/`cm`/`m` | 单托尺寸 |
| form_mode | String | 否 | 免账号**不要**传 `logged_in_quick` | 省略即为匿名 |
| registration | Object | 否 | 见附录 | DOM 回退时可选自填弱注册信息 |
\* `total_weight``total_weight_lb` 二选一即可。
**硬限(换算后)**
| 项 | 限制 |
|----|------|
| 总重 | ≤ 45000 lb |
| 尺寸 | L≤636W≤102H≤108in |
| 线性英尺 | 整票占用 ≤ 约 53'(否则校验失败) |
**请求示例**
```json
{
"request_id": "a1b2c3d4-e5f6-4789-a012-3456789abcde",
"customer_id": "CUST_001",
"flock_input": {
"pickup_date": "07/22/2026",
"pickup_zip": "90001",
"delivery_zip": "75201",
"pallet_count": 6,
"total_weight": { "value": 12000, "unit": "lb" },
"dimensions": { "length": 48, "width": 40, "height": 48, "unit": "in" }
}
}
```
#### 提交成功响应
```json
{
"code": 0,
"message": "ok",
"data": {
"quote_id": "QTE_20260721_0201",
"status": "processing"
}
}
```
### 7.3 结果字段(轮询成功后)
| 字段 | 类型 | 说明 |
|------|------|------|
| data.flock.reference | String | Flock 参考号(可能为空) |
| data.flock.lines[] | Array | 报价行 |
| data.flock.lines[].tier | String | `flock_direct` / `standard` |
| data.flock.lines[].label | String | 展示名 |
| data.flock.lines[].transit_days / transitDays | String | 时效 |
| data.flock.lines[].final_total_usd | Number | **对客户展示价USD已含加价** |
| data.flock.shipment_meta | Object | 提货日、邮编、重量等摘要 |
> 一档即可能成功;勿假设固定两档。展示价优先 `final_total_usd`。
---
## 8. 路线 DFlock 登录后(`FLOCK_LOGGED_IN`
适用:客户**已**绑定 Flock 账密;参数按登录后 Quick 表单填写。
服务端强制官网登录 DOM**禁止**匿名 Direct。宿主须传 `form_mode: "logged_in_quick"`,并尽量补齐登录后字段,否则 RPA 可能填表失败。
### 8.1 调用流程
与 §7.1 相同(同一对接口),差异仅在请求体与服务端执行路径。
### 8.2 提交询价
#### 接口地址
`POST /api/flock/quotes`(同路线 C
#### flock_input 相对免账号的差异
| 字段 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|
| form_mode | String | **是(本路线)** | 固定 `logged_in_quick` | 切换登录后校验与 RPA 表单 |
| pallet_count | Number | 是 | **1999** | 登录后件数上限放宽 |
| pickup_location_type | String | 建议 | 官网地点类型 | 如商业/住宅等 id/文案 |
| delivery_location_type | String | 建议 | 同上 | 派送地点类型 |
| packaging_type | String | 建议 | 包装类型 | 登录后 Quick 字段 |
| freight_class | String | 建议 | 货运等级 | 登录后 Quick 字段 |
| description | String | 建议 | 货物描述 | 登录后 Quick 字段 |
| stackable / turnable | Boolean | 否 | | 可堆叠 / 可翻转 |
| additional_services | Array\<String\> | 否 | | 附加服务 id |
| vehicle_types | Array\<String\> | 否 | | 允许车型 id |
| pickup_liftgate / pickup_inside / pickup_pallet_jack | Boolean | 否 | | 提货附属 |
| delivery_liftgate / delivery_inside / delivery_pallet_jack | Boolean | 否 | | 派送附属 |
| call_before_pickup / call_before_delivery | Boolean | 否 | | 提货/派送前电话 |
| pickup_service / delivery_service 及窗口时间字段 | String | 否 | 总重 >5000 lb 时官网才开放高级调度 | 见实现字段名 |
> 登录后**不再强制**免账号的 420 托盘与线性英尺校验;尺寸/总重上限仍适用 Flock 官网硬限。
**请求示例**
```json
{
"request_id": "b2c3d4e5-f6a7-4890-b123-456789abcdef",
"customer_id": "CUST_001",
"flock_input": {
"form_mode": "logged_in_quick",
"pickup_date": "07/22/2026",
"pickup_zip": "90001",
"delivery_zip": "75201",
"pallet_count": 2,
"total_weight": { "value": 2200, "unit": "lb" },
"dimensions": { "length": 48, "width": 40, "height": 48, "unit": "in" },
"packaging_type": "Pallets",
"freight_class": "70",
"description": "General freight",
"pickup_location_type": "Business with a dock or forklift",
"delivery_location_type": "Business with a dock or forklift"
}
}
```
结果字段同 §7.3。
---
## 9. 通用:异步轮询
适用于 `MS_LOGGED_IN`、`FLOCK_GUEST`、`FLOCK_LOGGED_IN`(以及非推荐的 MotherShip 异步三步)。
### 9.1 接口地址
`GET /api/quotes/{quote_id}`
### 9.2 请求
- 方法GET
- 路径参数:`quote_id`(提交接口返回)
- 鉴权头:同 §2.3(须为报价所属客户)
### 9.3 响应数据
| 字段 | 类型 | 说明 |
|------|------|------|
| data.quote_id | String | 报价单号 |
| data.request_id | String | 请求标识 |
| data.status | String | `processing` / `done` / `failed` / `expired` |
| data.currency | String | `USD` |
| data.quotes | Array | MotherShip 档位MS_* 成功时) |
| data.flock | Object | Flock 结果FLOCK_* 成功时) |
| data.error_code / error_message | String | 失败时 |
| data.rpa_stage / rpa_stage_label | String | processing 时可能出现的进度 |
| data.valid_until | String | 成功时有效期 |
### 9.4 轮询建议
| 项 | 建议 |
|----|------|
| 间隔 | 2 s10 s 后可加密至约 0.8 s |
| MotherShip 总窗口 | ≥ `420000` ms |
| Flock 总窗口 | ≥ `210000` ms以联调环境配置为准 |
| 终止条件 | `done` / `failed` / `expired` / 超时 |
| 并发 | 同一用户操作防重复提交 |
---
## 10. 错误码
### 10.1 接口层错误(`code !== 0`
| code | HTTP | 说明 | 处理建议 |
|------|------|------|----------|
| UNAUTHORIZED | 401 | 缺少或无效 API Key | 检查 Bearer / 轮换 |
| FORBIDDEN | 403 | 客户与 Key 不匹配,或无权访问报价 | 核对 `X-Customer-Id` / `quote_id` |
| VALIDATION_FAILED | 400 | 字段不合法 | 按**当前路线**硬限与必填项检查 |
| SESSION_EXPIRED | 404 | `host_session_id` 过期 | 仅 MS_GUEST重走候选 |
| ADDRESS_SESSION_MISMATCH | 400 | 候选不属于本次会话 | 使用返回的完整对象 |
| ADDRESS_NOT_SELECTABLE | 400 | 提交了不可选候选 | 改选 `selectable: true` |
| QUOTE_NOT_FOUND | 404 | 报价不存在 | 检查 quote_id |
| QUOTE_TIMEOUT | 504 | 同步等待超时 | 稍后重试 |
| RATE_LIMITED | 429 | 限流 | 降频 |
| QUOTE_UNAVAILABLE | 503 | 渠道不可用(如 Flock 关闭/占用) | 稍后重试或换路线 |
| INTERNAL_ERROR | 5xx | 系统异常 | 稍后重试 |
### 10.2 询价业务失败(`code = 0` 且 `data.status = failed`
| error_code | 说明 |
|------------|------|
| QUOTE_UNAVAILABLE | 暂时无法获取报价 |
| QUOTE_TIMEOUT | 询价超时 |
| QUOTE_ENTRY_UNAVAILABLE | 报价入口暂时无法打开 |
| ADDRESS_SUGGESTION_NOT_FOUND | 地址无法匹配 |
| CARRIER_NO_CAPACITY | 该线路暂无可用运力 |
| ADDRESS_NOT_SUPPORTED | 地址不在服务范围 |
| RPA_CAPTCHA | 触发验证码 |
| SESSION_EXPIRED | 承运商登录会话过期 |
| PROVIDER_LOGIN_FAILED | 承运商账密不正确或未即时更新 |
| RPA_DATA_INVALID | 未能解析完整报价 |
| PAGE_LOAD_TIMEOUT | 承运商页面加载超时 |
> 对外展示优先使用中文 `error_message`。
---
## 11. 对接建议
### 11.1 第三方需要做
1. 服务端保存 API Key 与 Customer ID后端代理调用。
2. 实现路线判定§3**一票一路**。
3. 账密由管理端绑定;有账密则按登录后字段规范传参。
4. `MS_GUEST`:地址选择 UI + submit Loading≥420 s
5. 异步路线实现轮询与超时Flock 读 `flock.lines`MotherShip 读 `quotes`
6. 成功后注意 `valid_until`
### 11.2 第三方不需要做
1. 不需要在请求体传官网密码。
2. 不需要自行做单位换算(支持 `lb`/`kg`/`t`、`in`/`cm`/`m`,服务端统一进一取整为 lb/in
3. 不需要调用管理端客户 CRUD / embed-demo 登录作为正式对接。
4. 不需要把 Priority1 实验接口当作本期正式四路线之一。
### 11.3 cURL 示例
**MS_GUEST 第一步**
```bash
curl -X POST "https://if.dev.51track.vip/api/host/quote/candidates" \
-H "Authorization: Bearer <API Key>" \
-H "X-Customer-Id: CUST_001" \
-H "Content-Type: application/json" \
--data-binary @ms-guest-candidates.json
```
**FLOCK_GUEST 提交**
```bash
curl -X POST "https://if.dev.51track.vip/api/flock/quotes" \
-H "Authorization: Bearer <API Key>" \
-H "X-Customer-Id: CUST_001" \
-H "Content-Type: application/json" \
--data-binary @flock-guest.json
```
**轮询**
```bash
curl -X GET "https://if.dev.51track.vip/api/quotes/QTE_20260721_0201" \
-H "Authorization: Bearer <API Key>" \
-H "X-Customer-Id: CUST_001"
```
### 11.4 Apifox 环境变量建议
| 变量 | 示例值 |
|------|--------|
| baseUrl | `https://if.dev.51track.vip` |
| token | `chj_...` |
| customerId | 与 Token 绑定的客户 ID |
Headers`Authorization: Bearer {{token}}``X-Customer-Id: {{customerId}}``Content-Type: application/json`。
---
## 12. 附录
### 12.1 货物类型枚举cargo_typeMotherShip
| 值 | 说明 |
|----|------|
| general_freight | 普通货物(默认) |
| machinery | 机械设备 |
| furniture | 家具 |
| electronics | 电子产品 |
| building_materials | 建材 |
| auto_parts | 汽车配件 |
| food_nonperishable | 非生鲜食品 |
| apparel | 服装 |
| other | 其他 |
### 12.2 报价状态status
| 值 | 说明 |
|----|------|
| processing | 询价进行中(须继续轮询) |
| done | 成功 |
| failed | 失败,看 error_message |
| expired | 报价已过期 |
### 12.3 Flock registration可选免账号 DOM 回退)
| 字段 | 类型 | 说明 |
|------|------|------|
| first_name / last_name | String | 姓名 |
| company | String | 公司 |
| email | String | 工作邮箱 |
| phone | String | 10 位美国电话 |
| shipments_per_month | String | `1-25` / `26-50` / `51-100` / `101+` |
### 12.4 非推荐 / 不在本期四路线
| 说明 | 接口 |
|------|------|
| MotherShip 免账号异步三步(可用,非默认同步方案) | `POST /api/quotes` + 轮询 |
| Priority1实验 | `POST /api/priority1/quotes` |
| `/embed-demo` 演示门户 | Cookie 登录,非正式 OpenAPI |
| 管理端客户 / 预警等 | 仅运维 |
---
## 13. 联系与支持
| 事项 | 说明 |
|------|------|
| 开通客户 / API Key / 绑定官网账密 | 联系我方运维或管理端操作 |
| Key 失效 | 轮换后立即通知宿主更换 |
| 故障排查 | 提供时间、路线 ID、`quote_id` 或 `host_session_id`、请求摘要(勿传完整 Key/密码) |
| 文档版本 | 1.12026-07-21 |