Skip to content

Method 5: API integration

For teams with their own order system, or requirements the other four methods can't meet. The only difference from platform and ERP sync: this one needs code. High volume but no appetite for development? See Method 2: bulk import. Orders sitting in a marketplace or a third-party ERP? See Methods 3-4: automatic feeds.

When the API is actually worth it

Where your orders liveWhat to use
Amazon, Shopify, Rakuten and similar marketplacesPlatform sync, no development
A third-party ERP you already runERP sync, no development
Just too many orders to key in by handBulk import, no development
Your own order system, or custom fields and timingAPI integration

What you need before you start

ThingWhere it comes from
appKey / appSecretOMS top right: avatar → API信息 (API information) (/userCenter/apiKeys)
API documentationhttps://apidoc-oms.xlwms.com/
Base URLhttps://api.xlwms.com/openapi/

The page is a table of App Key / App Secret / note / created at / created by, with the keys masked by default. A signature tool button in the corner takes you straight to the official verifier.

Where to find appKey and appSecret
Note appSecret is the signing key. Keep it server-side only — never in front-end code or a mobile app.

Signing a request

Every request carries an authcode signature, computed with HmacSHA256:

  1. Sort the fields inside the business payload data in dictionary order (case-insensitive) and serialise to JSON
  2. Concatenate appKey + the sorted data + reqTime, with no separator between them
  3. HmacSHA256 that string using appSecret as the key and hex-encode the result — that's your authcode

Where each parameter goes matters too: authcode travels as a URL query parameter, while appKey / data / reqTime go in the POST body.

ParameterLocationNotes
authcodeURL queryThe signature computed above
appKeyPOST bodyIssued in the OMS console
reqTimePOST body10-digit UNIX timestamp in seconds
dataPOST bodyThe business payload; an array for the create endpoint
Important Requests whose reqTime is more than 5 minutes old are rejected by the server. Don't replay cached signatures, and keep your server clock in sync.

Signature not matching? Use the official verifier

The documentation site has an online signature tool. Paste in your appKey, data and reqTime and compare — it tells you immediately whether sorting, serialisation or concatenation is the culprit.

Signing example

A minimal working implementation of what the documentation describes. Check it against the official verifier before you start integration testing.

js
// Node.js 18+
import crypto from 'node:crypto'

function sign({ appKey, appSecret, data, reqTime }) {
  // 1. data 内字段按字典序(不区分大小写)排序后序列化
  const sortedData = JSON.stringify(sortKeys(data))
  // 2. 三段直接拼接,没有分隔符
  const raw = `${appKey}${sortedData}${reqTime}`
  // 3. HmacSHA256 后转十六进制
  return crypto.createHmac('sha256', appSecret).update(raw).digest('hex')
}

function sortKeys(value) {
  if (Array.isArray(value)) return value.map(sortKeys)
  if (value && typeof value === 'object') {
    return Object.fromEntries(
      Object.keys(value)
        .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
        .map((k) => [k, sortKeys(value[k])])
    )
  }
  return value
}

const appKey = process.env.OMS_APP_KEY
const appSecret = process.env.OMS_APP_SECRET
const reqTime = String(Math.floor(Date.now() / 1000))
const data = [
  {
    whCode: 'TEST',
    thirdOrderNo: 'SO-20260903-0001',
    subOrderType: 1,
    logisticsChannel: 'T_YAMATO_BIN',
    receiver: '佐藤 健太',
    telephone: '09012345678',
    countryRegionCode: 'JP',
    provinceName: '東京都',
    cityName: '渋谷区',
    postCode: '150-0002',
    addressOne: '渋谷 3-15-7',
    isSubmit: 1,
    itemList: [{ sku: 'APP-TSHIRT-006', quantity: 1 }]
  }
]

const authcode = sign({ appKey, appSecret, data, reqTime })
const res = await fetch(
  `https://api.xlwms.com/openapi/v1/outboundOrder/create?authcode=${authcode}`,
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ appKey, reqTime, data })
  }
)
console.log(await res.json())
python
# Python 3
import hashlib, hmac, json, os, time, requests

def sort_keys(value):
    if isinstance(value, list):
        return [sort_keys(v) for v in value]
    if isinstance(value, dict):
        return {k: sort_keys(value[k]) for k in sorted(value, key=str.lower)}
    return value

def sign(app_key, app_secret, data, req_time):
    sorted_data = json.dumps(sort_keys(data), ensure_ascii=False, separators=(',', ':'))
    raw = f'{app_key}{sorted_data}{req_time}'
    return hmac.new(app_secret.encode(), raw.encode(), hashlib.sha256).hexdigest()

app_key, app_secret = os.environ['OMS_APP_KEY'], os.environ['OMS_APP_SECRET']
req_time = str(int(time.time()))
data = [{
    'whCode': 'TEST',
    'thirdOrderNo': 'SO-20260903-0001',
    'subOrderType': 1,
    'logisticsChannel': 'T_YAMATO_BIN',
    'receiver': '佐藤 健太',
    'telephone': '09012345678',
    'countryRegionCode': 'JP',
    'provinceName': '東京都',
    'cityName': '渋谷区',
    'postCode': '150-0002',
    'addressOne': '渋谷 3-15-7',
    'isSubmit': 1,
    'itemList': [{'sku': 'APP-TSHIRT-006', 'quantity': 1}],
}]

authcode = sign(app_key, app_secret, data, req_time)
r = requests.post(
    'https://api.xlwms.com/openapi/v1/outboundOrder/create',
    params={'authcode': authcode},
    json={'appKey': app_key, 'reqTime': req_time, 'data': data},
)
print(r.json())

Creating a parcel outbound order

POST /v1/outboundOrder/create, up to 100 orders per request.

The main fields (the official docs are authoritative for the full list):

FieldRequiredField on screenNotes
whCode✳︎WarehouseWarehouse code, max 30
thirdOrderNo✳︎——External order number, globally unique, max 100
subOrderType✳︎——1 = by product, 2 = by box
logisticsChannel✳︎Logistics channelChannel code, e.g. T_YAMATO_BIN
referOrderNoReference NumberYour own system's id
platformOrderNoPlatform Order NumberThe marketplace order number
isSubmit——0 = save as draft (default), 1 = submit straight away
remarkNoteThe API allows 255, but the carrier only takes 32 full-width
receiver✳︎Recipient
telephonetelephoneOptional in the API, but mandatory for Yamato and Sagawa
countryRegionCode✳︎Country/RegionAlways JP for Japan
provinceName✳︎Province/StateThe prefecture for Japan, e.g. 東京都
provinceCode✳︎Province/StateRequired for the US, Canada and Australia
cityName✳︎City NameThe ward / municipality for Japan
postCode✳︎Postal code
addressOne✳︎Address1The API allows 255, but the carrier only takes 16 full-width
addressTwoAddress2Same, 16 full-width
districtDistrict / CountyLeave empty for Japan
itemList[].sku✳︎SKU
itemList[].quantity✳︎Outgoing Quantity
Same as Method 1 The business rules are identical to creating an order in the console — how a Japanese address maps onto the fields, which carriers demand a phone number, the character limits. See Method 1. The API's own limits are looser than the carrier's, so a payload the API accepts can still fail at the carrier.Note isSubmit decides where the order lands. With 1 it is submitted immediately, and from there the channel decides: Yamato goes straight to Awaiting, Sagawa stops in Drafts → To be submitted. See Step 6.

The other endpoints you'll need

PurposeEndpoint
Paginated list of outbound ordersgetParcelOutboundOrders
Order detail (read back status and tracking number)getOutboundOrder
Batch cancel orders (asynchronous)batchCancelOutboundOrder
Aggregated order status (confirm a cancellation)selectBizStatus
Update tracking number or label (when using your own)updateTrackNoAndLabel
Add a message or attachment to an orderreplyBigOutboundMessage

Three traps to avoid

Important HTTP 500 does not mean the order wasn't created. The official docs say a network error must not be treated as a failure — retrying blindly duplicates orders. Query the detail endpoint by thirdOrderNo first, and only retry once you've confirmed nothing was created.Note code: 200 only means the request succeeded, not that every order in it was created. Check data[].success and data[].msg per order.Note thirdOrderNo is globally unique — use it as your idempotency key. Re-sending the same external number won't create a duplicate, which is the safety net your retry logic needs.

Freight outbound (ToB) uses different endpoints

Everything on this page is about parcel outbound. Freight Outbound (ToB) is a separate set: createBigOutboundOrder (by product) and createBoxStockOutboundOrder (by box), with their own query and cancel endpoints.

The official docs are authoritative

This page covers the main integration path for parcel outbound orders and the traps that catch people out. For the complete field list, error codes and the endpoints for other business flows, see https://apidoc-oms.xlwms.com/

Best used alongside the LingXing OMS documentation