Skip to content

API Authentication & Signature

All OpenAPI module endpoints require an MD5 token. Every request must include three HTTP headers.

Required Headers

HeaderDescription
x-auth-nameAPI client name issued by WMG
x-auth-seed13-digit millisecond Unix timestamp (e.g., 1746700000000)
x-auth-tokenMD5 authentication token (see formula below)

Seed

The platform clock runs on Singapore time (UTC+08:00). The x-auth-seed window is checked against that clock.

Rules

  • 13-digit numeric Unix timestamp in milliseconds — not seconds, and not a formatted date string.
  • Must be within ±10 minutes of platform time, otherwise the request is rejected with x-auth-seed: TIMEOUT.
  • Generate it immediately before sending the request, not once at script startup.

A Unix timestamp is an absolute instant, so the correct value is the same number whether your machine is set to Singapore time or to any other zone — provided your system clock is accurate and your client converts it properly. Each method listed below yields the value that matches Singapore platform time.

x-auth-seedSingapore time (UTC+08:00)
17467000000002025-05-08 18:26:40 +08:00
17537530690002025-07-29 09:37:49 +08:00

Generating the seed

LanguageUse
BashSEED=$(date +%s%3N)
PowerShell$SEED = [string]([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds())
Pythonseed = str(int(time.time() * 1000))
Node.jsconst seed = String(Date.now());
PHP$SEED = (string)(time() * 1000);

PowerShell: do not use Get-Date -UFormat %s

It returns a 10-digit seconds value, which is rejected with x-auth-seed: LENGTH MUST BE 13 DIGITS. On Windows PowerShell 5.1 it is additionally derived from local time — on a machine set to Singapore time it runs 28800 seconds (8 hours) ahead of the real Unix timestamp.

powershell
# Windows PowerShell 5.1, machine set to Singapore Standard Time
Get-Date -UFormat %s                                 # 1785149525     <- seconds, and 8 hours ahead
[DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()    # 1785120724000  <- correct

PowerShell 7 returns the correct instant for both, so the clock fault only surfaces once the same script runs under 5.1 — but the digit count is wrong in either version. Always use [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds().

Token Formula

x-auth-token = md5( strtolower(api_name) + api_key + seed )

The result is a lowercase hex string (32 characters). No request parameters are included in the token.

Step-by-Step

  1. Lowercase the API name — convert x-auth-name to lowercase.
  2. Concatenate the lowercased name, your api_key, and the seed string — no separators.
  3. MD5 hash the concatenated UTF-8 string.
  4. Hex-encode the raw binary hash to produce a 32-character lowercase string.
  5. Place the result in the x-auth-token header.

Note: The seed must be a string when concatenated — do not convert to an integer first. The MD5 input is the raw UTF-8 bytes of the concatenated string.

Reference Implementations

Bash

bash
API_NAME="your_api_name"
API_KEY="your_api_key"
SEED=$(date +%s%3N)
TOKEN=$(printf '%s' "$(echo -n "$API_NAME" | tr '[:upper:]' '[:lower:]')${API_KEY}${SEED}" | md5sum | cut -d' ' -f1)

curl -G "https://api.test.wmgdelivery.com/v1/openapi/station/get-stations" \
  -H "x-auth-name: $API_NAME" \
  -H "x-auth-seed: $SEED" \
  -H "x-auth-token: $TOKEN"

Windows PowerShell

powershell
$API_NAME = "your_api_name"
$API_KEY  = "your_api_key"
$SEED     = [string]([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds())

$raw   = [System.Text.Encoding]::UTF8.GetBytes($API_NAME.ToLower() + $API_KEY + $SEED)
$md5   = [System.Security.Cryptography.MD5]::Create().ComputeHash($raw)
$TOKEN = -join ($md5 | ForEach-Object { $_.ToString("x2") })

$headers = @{
    "x-auth-name"  = $API_NAME
    "x-auth-seed"  = $SEED
    "x-auth-token" = $TOKEN
}

$response = Invoke-RestMethod -Uri "https://api.test.wmgdelivery.com/v1/openapi/station/get-stations" -Method Get -Headers $headers
$response | ConvertTo-Json -Depth 10

Python

python
import time, hashlib
import requests

API_NAME = 'your_api_name'
API_KEY  = 'your_api_key'


def build_token(api_name: str, api_key: str, seed: str) -> str:
    """seed must be the millisecond timestamp as a string — do not convert to int."""
    raw = api_name.lower() + api_key + seed
    return hashlib.md5(raw.encode()).hexdigest()


seed  = str(int(time.time() * 1000))
token = build_token(API_NAME, API_KEY, seed)

resp = requests.get(
    'https://api.test.wmgdelivery.com/v1/openapi/station/get-stations',
    headers={
        'x-auth-name':  API_NAME,
        'x-auth-seed':  seed,
        'x-auth-token': token,
    },
)
print(resp.json())

Node.js / TypeScript

Uses only the built-in node:crypto module and the global fetch (Node.js 18+).

typescript
import crypto from 'node:crypto';

const API_NAME = 'your_api_name';
const API_KEY  = 'your_api_key';

function buildToken(apiName: string, apiKey: string, seed: string): string {
  // seed must remain a string; concatenation runs over raw UTF-8 bytes
  return crypto.createHash('md5')
    .update(apiName.toLowerCase() + apiKey + seed)
    .digest('hex');
}

const seed  = String(Date.now());
const token = buildToken(API_NAME, API_KEY, seed);

const resp = await fetch('https://api.test.wmgdelivery.com/v1/openapi/station/get-stations', {
  headers: {
    'x-auth-name':  API_NAME,
    'x-auth-seed':  seed,
    'x-auth-token': token,
  },
});
console.log(JSON.stringify(await resp.json(), null, 2));

PHP

php
<?php

function build_token(string $api_name, string $api_key, string $seed): string
{
    return md5(strtolower($api_name) . $api_key . $seed);
}

$API_NAME = 'your_api_name';
$API_KEY  = 'your_api_key';
$SEED     = (string)(time() * 1000);   // 13-digit millisecond timestamp
$TOKEN    = build_token($API_NAME, $API_KEY, $SEED);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.test.wmgdelivery.com/v1/openapi/station/get-stations');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'x-auth-name: '  . $API_NAME,
    'x-auth-seed: '  . $SEED,
    'x-auth-token: ' . $TOKEN,
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
curl_close($ch);
echo json_encode(json_decode($response, true), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n";
?>

Usage Example

Input Parameters

API credentials:

api_name = "Demo_Client"
api_key  = "abc"

Seed (millisecond timestamp):

seed = "1746700000000"

Execution

php
$token = build_token('Demo_Client', 'abc', '1746700000000');

Result

Concatenated input string:

demo_clientabc1746700000000

MD5 hash result:

6e7190914323a4b7e2f94ba798a3f015

This hash is reproducible — feeding the same three inputs (Demo_Client, abc, 1746700000000) to any MD5 implementation always yields exactly this value. Use it as a fixture to validate your own implementation before going live.

Step-by-Step Breakdown of Example

1. Lowercase the API Name

"Demo_Client" → "demo_client"

2. Concatenate

"demo_client" + "abc" + "1746700000000"
= "demo_clientabc1746700000000"

3. MD5 Hash

Compute MD5 of the UTF-8 bytes of the concatenated string (binary output), then hex-encode:

md5("demo_clientabc1746700000000") → 6e7190914323a4b7e2f94ba798a3f015

Integration into API Requests

Once generated, include the token in your API requests:

  1. Set your API name in the x-auth-name HTTP header
  2. Set the seed in the x-auth-seed HTTP header
  3. Set the computed token in the x-auth-token HTTP header

Example:

x-auth-name:  Demo_Client
x-auth-seed:  1746700000000
x-auth-token: 6e7190914323a4b7e2f94ba798a3f015

Token Verification Tips

IssueLikely Cause
x-auth-token: INVALIDAPI name not lowercased before concatenation; wrong key or seed used
x-auth-seed: TIMEOUTSeed generated at script startup instead of just before the request; client clock drift; or Get-Date -UFormat %s on Windows PowerShell 5.1 (see Seed)
x-auth-seed: LENGTH MUST BE 13 DIGITSSeconds sent instead of milliseconds (10 digits) — multiply by 1000, see Seed

Error Responses

Error MessageCause
x-auth-seed: FORMAT ERRORSeed is not numeric
x-auth-seed: LENGTH MUST BE 13 DIGITSSeed is not exactly 13 digits
x-auth-seed: TIMEOUTSeed differs from platform time by more than 10 minutes
x-auth-name: INVALIDapi_name not found or account disabled
x-auth-token: INVALIDToken mismatch

Standard Response Envelope

All endpoints return JSON with the following envelope:

json
{
  "code": 0,
  "message": "success",
  "data": {}
}
FieldTypeDescription
codeinteger0 = success, 1 = error
messagestringHuman-readable message
dataobject/arrayResponse payload; [] on error

Unexpected server-side failures are also returned as code: 1 with the message something error.