Ganymede Boston
APIv1

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-supplied external_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

提示: Key 只保存到 localStorage · 不会上传我们服务器。刷新页面后自动加载。 Note: The key is stored only in your browser's localStorage. It is not uploaded anywhere. Reloading the page will load it back.
POST
Path
请求体 (JSON)Body (JSON)

认证与授权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.

重要:Important: API Key 仅在创建时展示一次 · 请立即保存到密钥管理系统 (环境变量 / Vault / K8s Secret 等) · 不要写死在代码里 · 不要提交到 Git。 API keys are displayed only once at creation. Store them in a secrets manager (environment variable, Vault, AWS Secrets Manager). Never commit keys to source control.

权限范围 (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_authAuthorization 请求头Missing Authorization header
invalid_key_formatKey 不是 gm_ 开头Key does not start with gm_
invalid_keyKey 不存在Key is unknown
key_revokedKey 已吊销 · 申请新的Key was revoked, request a new one
insufficient_scopeKey 缺此操作所需 scopeKey lacks required scope
missing_external_idPOST body 缺 external_idMissing external_id
missing_delivery_noPOST body 缺 delivery_noMissing delivery_no
missing_itemsitems 数组为空items array is empty
invalid_item某条 item 缺 skudeclare_num 非法Item missing sku or has invalid declare_num
invalid_json请求体不是合法 JSONBody is not valid JSON
already_receivedASN 已到货 · 不允许修改ASN already received; cannot modify
unsupported_actionPATCH 的 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

idinteger
Ganymede 系统内部主键。Unique internal identifier assigned by Ganymede.
external_idstring · required
调用方指定的唯一 ID · 也作幂等键 —— 用相同 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.
delivery_nostring · required
跟踪号 (Tracking Number) · 仓库人员扫的就是这个。Tracking number of the shipment. This is what warehouse staff scans upon arrival.
removal_order_nostring · optional
亚马逊移除订单号 (Amazon Removal Order Number)。Amazon Removal Order Number, if applicable.
shipperstring · optional
承运商名称 · UPS / USPS / FedEx / DHL 等。Carrier name — UPS, USPS, FedEx, DHL, etc.
estimated_arrival_timedate · optional
预计到货日期 · ISO 8601 格式 (YYYY-MM-DD)。Expected arrival date, ISO 8601 format (YYYY-MM-DD).
shippment_timedatetime · optional
发货时间戳 · ISO 8601 datetime。Ship-out timestamp, ISO 8601 datetime.
addressobject · optional
收货地址 · 自由 JSON 结构 · 我方原样存储。Recipient address as a free-form JSON object. Structure is not enforced.
remarkstring · optional
备注 · 自由文本。Free-form notes.
itemsarray · required
预期 SKU 列表 · 至少 1 条 · 每条结构见下。List of expected SKUs. Must contain at least one item. See item schema below.
local_statusenum
服务端维护 · 枚举值: pending (待收) · received (已收 · 完全匹配) · discrepancy (已收但有差异) · cancelled (已取消)。Server-managed. One of pending, received, discrepancy, cancelled.
received_atdatetime · nullable
仓库收货完成时间 · 未到货时为 nullTimestamp when warehouse completed receiving. null until arrival.

Item 结构Item schema

skustring · required
SKU 主键 · 匹配我方产品目录。SKU identifier. Matched against your Ganymede product catalog.
declare_numinteger · required
申报数量 · 非负整数。Expected quantity, non-negative integer.
product_namestring · optional
产品名 · SKU 首次出现时创建产品记录用。Human-readable product name. Used if the SKU is new to our catalog.
mskustring · optional
亚马逊 MSKU (Merchant SKU)。Amazon Merchant SKU.
fnskustring · optional
亚马逊 FNSKU (Fulfillment Network SKU)。Amazon Fulfillment Network SKU.
local_received_qtyinteger · nullable
仓库扫码实收数量 · 服务端维护 · 未到货前为 nullServer-managed. Actual received quantity after warehouse scan. null until arrival.
varianceinteger · nullable
差异量 = 实收 − 申报 · 负值代表少收 · 正值代表多收。local_received_qty − declare_num. Negative for shortages, positive for over-receipts.

POST/v1/asn

创建 / 更新 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

201 Created 200 OK (幂等更新)(idempotent update) 400 invalid_item 409 already_received
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}
    ]
  }'

GET/v1/asn

查询 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

statusenum · optional
local_status 过滤 · pending / received / discrepancy / cancelledFilter by local_status: pending, received, discrepancy, or cancelled.
sincedatetime · optional
ISO 8601 时间戳 · 只返回此时间之后创建的。ISO 8601 timestamp. Return only ASNs created at or after this time.
limitinteger · optional
每页大小 · 默认 50 · 最大 200。Page size. Default 50, maximum 200.
offsetinteger · optional
跳过前 N 条 · 默认 0。Skip this many records. Default 0.

GET/v1/asn/:external_id

查询单条 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

external_idstring · required
创建时你提供的唯一 ID。The identifier you supplied when creating the ASN.

PATCH/v1/asn/:external_id

取消 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

actionenum · required
当前仅支持 cancelOnly cancel is supported at present.
限制:Restrictions: 已到货 ASN 无法取消 · 会返 409 already_received An already-received ASN cannot be cancelled; the endpoint returns 409 already_received.

GET/v1/inventory

查询库存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

skustring · optional
过滤单个 SKU。Filter to a single SKU.
limitinteger · optional
默认 100 · 最大 500。Default 100, maximum 500.
offsetinteger · optional
默认 0。Default 0.

GET/v1/health

健康检查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_noexternal_id幂等键Idempotency key
removal_order_noremoval_order_no亚马逊移除单号Amazon order number
delivery_nodelivery_no跟踪号Tracking number
shippershipper承运商Carrier
estimated_arrival_timeestimated_arrival_time预计到货ISO date
shippment_timeshippment_time发货时间ISO datetime
addressaddress原样传递Pass through as-is
remarkremark
product[].skuitems[].sku
product[].mskuitems[].msku
product[].fnskuitems[].fnsku
product[].product_nameitems[].product_name
product[].declare_numitems[].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_KEYGanymede 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.