Ganymede WMS APIGanymede WMS API
面向亚马逊卖家的第三方海外仓 RESTful HTTP 接口。JSON 编码 · Bearer Token 认证 · 用于推送移除单预报 (ASN) 和查询实时库存。 RESTful HTTP API for third-party warehousing serving Amazon sellers. JSON in, JSON out. Bearer-token authenticated. Push Advance Shipping Notices (ASN) and query real-time inventory.
Ganymede WMS 是位于美国东海岸 (Massachusetts · Boston) 的自研仓储管理系统。本 API 用于把移除单预报数据推送到我方系统 —— 我们仓库收货时按跟踪号自动匹配预期 SKU 与数量 · 差异自动记录并生成收货回执 (GRN)。 Ganymede WMS is a self-built warehouse management system operating on the US East Coast (Massachusetts, Boston). This API is used to push Advance Shipping Notice data into our system — our warehouse matches incoming shipments to expected SKUs by tracking number, and variances are automatically recorded into a Goods Receipt Note (GRN).
接口根地址 (Base URL)Base URL
https://igexxkfaumyajenaricv.supabase.co/functions/v1/public-api
设计原则Design principles
- 资源导向的 URL (REST 风格)Resource-oriented URLs (REST)
- 标准 HTTP 动词 (GET / POST / PATCH)Standard HTTP verbs (GET / POST / PATCH)
- 机器可读的错误码 (error slug + 人类可读消息)Predictable error codes with machine-readable slug
- 通过调用方指定的
external_id实现幂等创建Idempotent creates via caller-suppliedexternal_id - Bearer Token 认证 · Scope 精细化授权Bearer-token authentication with scope-based authorization
接口调试台 API Playground
直接在浏览器里调用真实 API · 无需 curl 或 Postman · 便于快速验证认证和数据结构。 Call the live API directly from your browser — no curl or Postman needed. Ideal for quickly verifying auth and payload shape.
试一试 Try it 实时调用 Live
认证与授权Authentication
所有请求必须带 Authorization 请求头 · 附上有效的 API Key。
All requests must include an Authorization header with a valid API key.
Authorization: Bearer gm_live_XXXXXXXXXXXXXXXXXX...
API Key 由 Ganymede 管理员签发 · 邮件申请 support@ganymedebos.com · 邮件写明公司名、联系人、用途。通常在 1 个工作日内发出。 API keys are issued by Ganymede administrators upon request. Email support@ganymedebos.com with your company name, contact person, and intended use case. Keys are typically issued within one business day.
权限范围 (Scopes)Scopes
每把 Key 绑定一组 scopes · 管理员生成时勾选。缺权限返 403 insufficient_scope。
Each key is provisioned with a set of scopes controlling which endpoints it may call. Missing a scope returns 403 insufficient_scope.
| Scope | 允许的操作Grants |
|---|---|
asn:read | 查询预报单Read ASN records |
asn:write | 创建、修改、取消预报单Create, update, and cancel ASN records |
inventory:read | 查询实时库存 (6 桶分类)Query real-time inventory buckets |
receipts:read | 拉取收货回执 (GRN) · 2026 Q4 发布 Poll goods receipt notes (GRN). Available Q4 2026. |
请求追踪Request tracing
每个响应都带 x-request-id 响应头。反馈问题时请附上此 ID · 我们可以立刻定位日志。
Every response includes an x-request-id header. When reporting issues, please include this ID — it lets us locate the request in our audit log immediately.
错误处理Errors
Ganymede 使用标准 HTTP 状态码。错误响应体统一结构 · 含机器可读的 code 和人类可读的 message。
Ganymede uses conventional HTTP status codes. Errors always return a JSON body with a stable machine-readable code and a human-readable message.
{
"error": {
"code": "missing_external_id",
"message": "external_id is required",
"request_id": "req_a8c391f2b1d4"
}
}
HTTP 状态码HTTP status codes
| Code | 含义Meaning |
|---|---|
200 | 成功Success |
201 | 资源已创建Resource created |
204 | 成功 · 无响应体 (仅 CORS preflight)Success, no body (preflight only) |
400 | 请求格式错误 (缺字段 · 无效 JSON · 值不合法)Malformed request |
401 | 认证失败 (缺 / 无效 / 已吊销的 Key)Authentication failed |
403 | 认证通过但权限不足 (Scope 未授权)Authenticated but forbidden (missing scope) |
404 | 资源不存在或不属于当前调用方Resource not found or not owned by caller |
409 | 冲突 (如修改已收货 ASN)Conflict (e.g. modifying already-received ASN) |
500 | 服务器错误 · 请指数退避后重试Server error — retry with backoff |
错误 CodeError codes
| Code | 含义Meaning |
|---|---|
missing_auth | 缺 Authorization 请求头Missing Authorization header |
invalid_key_format | Key 不是 gm_ 开头Key does not start with gm_ |
invalid_key | Key 不存在Key is unknown |
key_revoked | Key 已吊销 · 申请新的Key was revoked, request a new one |
insufficient_scope | Key 缺此操作所需 scopeKey lacks required scope |
missing_external_id | POST body 缺 external_idMissing external_id |
missing_delivery_no | POST body 缺 delivery_noMissing delivery_no |
missing_items | items 数组为空items array is empty |
invalid_item | 某条 item 缺 sku 或 declare_num 非法Item missing sku or has invalid declare_num |
invalid_json | 请求体不是合法 JSONBody is not valid JSON |
already_received | ASN 已到货 · 不允许修改ASN already received; cannot modify |
unsupported_action | PATCH 的 action 值不支持Unsupported PATCH action |
not_found | 资源不存在Resource does not exist |
db_error · internal_error | 服务端异常 · 附 request_id 联系我们Server-side failure — attach request_id and contact us |
重试策略Retry policy
- 5xx: 指数退避重试 · 1s → 2s → 4s → 8s · 最多 5 次Exponential backoff, 1s → 2s → 4s → 8s, capped at 5 attempts
- 4xx: 不要重试 · 修请求后再发Do not retry — fix the request first
- Timeout: 建议客户端整体 timeout 设为 60sRecommended client timeout is 60 seconds
版本策略Versioning
当前 API 版本为 v1 · 稳定版 · 至少支持到 2027-12-31。
The current API version is v1. Stable, supported until at least 2027-12-31.
非破坏性变更 (新增 endpoint / 新增可选字段) 不另行通知。破坏性变更提前 30 天邮件通知所有活跃客户。 Non-breaking changes (new endpoints, new optional fields) ship without notice. Breaking changes are announced by email 30 days in advance.
3 步跑通Quickstart
从零到发出第一条 ASN · 约 5 分钟。 Go from zero to your first ASN in about 5 minutes.
1. 拿 API Key1. Obtain an API key
邮件 support@ganymedebos.com 申请 · 你会拿到一把 gm_live_XXXXXXXX... 格式的 Key。建议保存为环境变量 GANYMEDE_API_KEY。
Email support@ganymedebos.com to have a key issued. You will receive a key like gm_live_XXXXXXXX.... Store it as environment variable GANYMEDE_API_KEY.
2. 探活2. Verify connectivity
调无需认证的 health 接口: Call the health endpoint (no authentication required):
curl "https://igexxkfaumyajenaricv.supabase.co/functions/v1/public-api/v1/health"
3. 发出第一条 ASN3. Create your first ASN
最小请求 · 一条跟踪号 + 一个 SKU: Minimal request — one tracking number, one SKU:
curl -X POST "$BASE/v1/asn" \ -H "Authorization: Bearer $GANYMEDE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "TEST-2026-001", "delivery_no": "1Z999AA10123456784", "items": [{"sku": "ABC-123", "declare_num": 10}] }'
201 Created 说明 ASN 已在我方系统排队等收货。
A 201 Created response confirms the ASN is now waiting to be received at our warehouse.
ASN 对象模型The ASN object
代表一条移除单预报 —— 待到货的货件通知。通过 POST /v1/asn 创建 · 到货时按跟踪号自动匹配。
Represents an Advance Shipping Notice — the expected arrival of goods at our warehouse. Created via POST /v1/asn, matched against the tracking number upon arrival.
字段清单Attributes
external_id 重复调用会覆盖上一次而不是新建。Caller-supplied unique identifier. Used as the idempotency key — creating an ASN with the same external_id updates the previous one instead of duplicating.UPS / USPS / FedEx / DHL 等。Carrier name — UPS, USPS, FedEx, DHL, etc.YYYY-MM-DD)。Expected arrival date, ISO 8601 format (YYYY-MM-DD).pending (待收) · received (已收 · 完全匹配) · discrepancy (已收但有差异) · cancelled (已取消)。Server-managed. One of pending, received, discrepancy, cancelled.null。Timestamp when warehouse completed receiving. null until arrival.Item 结构Item schema
null。Server-managed. Actual received quantity after warehouse scan. null until arrival.local_received_qty − declare_num. Negative for shortages, positive for over-receipts.创建 / 更新 ASNCreate or update an ASN
注册一条预期到货货件。若同 external_id 已存在且尚未收货 · 字段和 items 会被完全覆盖。
Registers an expected shipment. If an ASN with the same external_id already exists and is not yet received, its fields and items are overwritten.
权限Scope
asn:write
请求体Body parameters
JSON body 匹配 ASN 对象模型。必填字段: external_id · delivery_no · items[]。
Send a JSON body matching the ASN object schema. Required fields: external_id, delivery_no, items[].
响应状态Responses
curl -X POST "$BASE/v1/asn" \ -H "Authorization: Bearer $KEY" \ -H "Content-Type: application/json" \ -d '{ "external_id": "OWR230725000004", "removal_order_no": "1000000123456", "delivery_no": "1Z999AA10123456784", "shipper": "UPS", "estimated_arrival_time": "2026-08-15", "items": [ {"sku": "ABC-123", "declare_num": 10, "product_name": "Widget A"}, {"sku": "XYZ-789", "declare_num": 5} ] }'
查询 ASN 列表List ASNs
分页返回调用方所有 ASN · 按创建时间倒序。 Returns a paginated list of ASNs belonging to the caller. Sorted by creation time, most recent first.
权限Scope
asn:read
查询参数Query parameters
local_status 过滤 · pending / received / discrepancy / cancelled。Filter by local_status: pending, received, discrepancy, or cancelled.查询单条 ASNRetrieve an ASN
返回单条 ASN + 完整 items · 含到货后的实收数量和 variance。 Returns a single ASN with its items, including received quantities and variance after arrival.
权限Scope
asn:read
路径参数Path parameter
取消 ASNCancel an ASN
把待收 ASN 标记为已取消 · 之后不再参与到货匹配。 Marks a pending ASN as cancelled. Once cancelled, an ASN will no longer be matched against arriving shipments.
权限Scope
asn:write
请求体Body parameter
cancel。Only cancel is supported at present.409 already_received。
An already-received ASN cannot be cancelled; the endpoint returns 409 already_received.
查询库存Query inventory
返回调用方的实时库存 · 按 Ganymede 内部 6 个库存桶细分。 Returns real-time inventory for the caller, broken down across six status buckets.
权限Scope
inventory:read
库存桶Buckets
| 字段Field | 桶含义Bucket |
|---|---|
qty_available | 良品 (可售)Sellable / grade-A stock |
qty_secondhand | 二手 (可售)Used goods held for resale |
qty_pending_outbound | 差品 / 待出Damaged / defect items awaiting disposition |
qty_relabel | 换标待处理Items pending relabel before release |
qty_storage | 一般仓储Long-term storage |
qty_in_repair | 维修中Currently under service / repair |
查询参数Query parameters
健康检查Health check
无需认证的探活接口 · 适合用于监控系统或桥接脚本部署后的冒烟测试。 Unauthenticated liveness probe. Use it in your uptime monitoring or as a smoke test after deploying a bridge script.
领星字段映射Lingxing field mapping
数据源来自领星 ERP 时 · 从 /erp/sc/routing/owms/removalInbound/list 拉到的字段按下表映射到我方。
If your ASN data source is Lingxing ERP, map fields from their /erp/sc/routing/owms/removalInbound/list endpoint to our schema as follows.
| 领星字段Lingxing field | Ganymede 字段Ganymede field | 说明Notes |
|---|---|---|
order_no | external_id | 幂等键Idempotency key |
removal_order_no | removal_order_no | 亚马逊移除单号Amazon order number |
delivery_no | delivery_no | 跟踪号Tracking number |
shipper | shipper | 承运商Carrier |
estimated_arrival_time | estimated_arrival_time | 预计到货ISO date |
shippment_time | shippment_time | 发货时间ISO datetime |
address | address | 原样传递Pass through as-is |
remark | remark | |
product[].sku | items[].sku | |
product[].msku | items[].msku | |
product[].fnsku | items[].fnsku | |
product[].product_name | items[].product_name | |
product[].declare_num | items[].declare_num | 整数Integer |
Python 桥接脚本Bridge script
可直接生产运行的 Python 参考实现 · 每小时从领星拉最近 30 天移除单 · 推送到 Ganymede。改 3 行环境变量即可上线。 A production-grade Python reference implementation that pulls the last 30 days of removal orders from Lingxing every hour and pushes them to the Ganymede API.
依赖: Python 3.8+ · pip install requests pycryptodome。设置以下环境变量:
Requirements: Python 3.8+, pip install requests pycryptodome. Configure the following environment variables:
| 变量名Variable | 用途Purpose |
|---|---|
LX_APP_ID | 领星 appIdLingxing appId |
LX_APP_SECRET | 领星 appSecretLingxing appSecret |
GANYMEDE_API_KEY | Ganymede API Key · gm_live_ 开头Ganymede API key (starts with gm_live_) |
#!/usr/bin/env python3 # lingxing_to_ganymede.py import os, json, hashlib, base64, requests, time from Crypto.Cipher import AES from Crypto.Util.Padding import pad LX_BASE = "https://openapi.lingxing.com" GM_BASE = "https://igexxkfaumyajenaricv.supabase.co/functions/v1/public-api" LX_APP_ID = os.environ["LX_APP_ID"] LX_APP_SECRET = os.environ["LX_APP_SECRET"] GM_KEY = os.environ["GANYMEDE_API_KEY"] def sign(params, app_id): to_str = lambda v: json.dumps(v, separators=(",",":")) if isinstance(v,(dict,list)) else str(v) keys = sorted(k for k,v in params.items() if v not in ("", None)) query = "&".join(f"{k}={to_str(params[k])}" for k in keys) md5 = hashlib.md5(query.encode()).hexdigest().upper() cipher = AES.new(app_id.encode(), AES.MODE_ECB) return base64.b64encode(cipher.encrypt(pad(md5.encode(), 16))).decode() def get_token(): r = requests.post(f"{LX_BASE}/api/auth-server/oauth/access-token", params={"appId": LX_APP_ID, "appSecret": LX_APP_SECRET}, timeout=30) return r.json()["data"]["access_token"] def fetch_asns(token): out, offset = [], 0 while True: biz = {"offset": offset, "length": 100, "status": 4} params = {**biz, "access_token": token, "app_key": LX_APP_ID, "timestamp": str(int(time.time()))} params["sign"] = sign(params, LX_APP_ID) r = requests.post(f"{LX_BASE}/erp/sc/routing/owms/removalInbound/list", params=params, json=biz, timeout=60) rows = r.json().get("data") or [] out.extend(rows) if len(rows) < 100 or offset >= 2000: break offset += 100 return out def to_ganymede(lx): return { "external_id": lx["order_no"], "removal_order_no": lx.get("removal_order_no"), "delivery_no": lx.get("delivery_no") or "", "shipper": lx.get("shipper"), "estimated_arrival_time": lx.get("estimated_arrival_time"), "items": [{ "sku": p["sku"], "msku": p.get("msku"), "fnsku": p.get("fnsku"), "product_name": p.get("product_name"), "declare_num": int(p.get("declare_num", 0)), } for p in lx.get("product", []) if p.get("sku")], } def push(asn): if not asn["delivery_no"] or not asn["items"]: return None r = requests.post(f"{GM_BASE}/v1/asn", headers={"Authorization": f"Bearer {GM_KEY}", "Content-Type": "application/json"}, json=asn, timeout=30) if r.status_code == 409: return None r.raise_for_status() return r.json() if __name__ == "__main__": token = get_token() for row in fetch_asns(token): push(to_ganymede(row))
部署方式Deployment
- Cron:
0 */1 * * * /usr/bin/python3 /opt/bridge/lingxing_to_ganymede.py - AWS Lambda + EventBridge: 打成 Lambda · EventBridge cron 每小时触发 · Secrets Manager 存 secret。 Package as Lambda, schedule hourly via EventBridge. Store secrets in AWS Secrets Manager.
-
Kubernetes CronJob:
schedule: "0 * * * *"· 加固镜像。schedule: "0 * * * *"on a hardened image.