| Item | Rule |
|---|
| Algorithm | HMAC-SHA256 |
| Signed content | The exact request-body JSON string (the raw payload, not reformatted in any way) |
| Secret | signKey, the shared signing key. Contact Support for test credentials; production credentials are assigned separately |
| Calculation | Signature = Base64(HMAC-SHA256(signKey, requestBody)) |
| Transport | HTTP request header Signature |
| Switch | Enabled by default, configurable per shop. Contact Support to change the shop setting. The create-shop endpoint is an exception and always verifies the signature |
POST /OpenApiV2/applyToken does not verify Signature; see Request an access token.
Standard headers for merchant business endpoints (everything except the token endpoint):
| Header | Value | Description |
|---|
Content-Type | application/json | Required |
Authorization | Bearer {token} | Token authentication. Do not send this header to the token endpoint |
Signature | {signature} | HMAC-SHA256 signature. Required when signing is enabled for the shop; not verified when disabled. Do not send it to the token endpoint |
OpenApiCreateUserAndShopV2 uses a token obtained with a platform SaaS app (appType=1), not a shop token; that token cannot call other merchant endpoints. This endpoint does not read the shop-level signature switch, so Signature is always required. See Create shop and AppAuth.
| Item | Rule |
|---|
| HTTP method | POST unless documented otherwise |
| Content-Type | application/json |
| Field naming | Mostly lower camelCase; a few fields keep historical spellings for compatibility (e.g. ItemPicInfoMain, Properties, CancelError) |
| Amounts | string, in the currency’s major unit (e.g. USD amounts in US dollars). Decimal places follow each field’s schema — some fields carry 4 decimals (e.g. the warranty quote price); do not truncate everything to 2 |
| Time format | RFC 3339, e.g. 2023-09-05T21:21:22+00:00 |
| Environment | Base URL |
|---|
| Beta (test) | https://betaxcottons.xmhhulu.com |
| Production (US) | https://openapi.xcottons.com |
The production domain resolves to two public entry IPs: 52.86.202.15 and 34.192.81.36. For global acceleration contact Support (accelerated domains have no fixed IPs).
| Item | Rule |
|---|
| Capability and switch | Supported; enablement is configured per shop and may be disabled for specific shops. Starting 2026-08-24, OpenAPI integrations enable replay protection by default. |
| Configuration | Contact Support to change the shop setting |
| Header | Format | Example |
|---|
Timestamp | Millisecond Unix timestamp | 1765952712000 |
Nonce | Random UUID string | d8193360-8ae1-4297-a092-13c0c8f4b7fe |
The difference between Timestamp and Xcotton server time must be within 300 seconds, otherwise the request is rejected.
Timestamp and Nonce are conditionally required: when replay protection is enabled for the shop, both must be present — omitting either fails validation. When disabled, they are not required and do not take part in signing.
| Item | Rule |
|---|
| Formula | Signature = Base64(HMAC-SHA256(signKey, newBody)) |
| New payload | newBody = the raw request-body JSON string byte-for-byte as sent, suffixed with the literal &Timestamp={timestamp}&Nonce={nonce} (& and = are literal characters, no spaces) |
| Byte scope | Do not re-serialize, escape, or reformat the body before concatenation; compute the HMAC over the complete concatenated string as UTF-8 bytes |
| Example | {"testkey":{"testkey1":"testvalue"}}&Timestamp=1765952712000&Nonce=d8193360-8ae1-4297-a092-13c0c8f4b7fe |
| Header | Value |
|---|
Content-Type | application/json |
Authorization | Bearer {token} |
Signature | {signature} |
Timestamp | {timestamp} |
Nonce | {nonce} |
The webhook URL is configured per shop during onboarding (openapiConfig.callEndpoints), with help from Support. Payload structures are shop-independent; only the URL is per-shop. Payloads for the six live webhook types and two design-draft types are documented in the OpenAPI webhooks section — see the API reference for fields. Those are not outbound endpoints; do not press Send against the OpenAPI hosts. The shipping-protection enablement callback uses the openApiSpSwitchCallback configuration key; its secret field is sensitive and must not be logged or exposed in a browser.
| Item | Rule |
|---|
| Method | POST |
| Content-Type | application/json |
| Verification header | xmh-hmc-signature (see §4.2) |
| Item | Rule |
|---|
| Algorithm | HMAC-SHA256 |
| Signed content | The request-body JSON string (raw payload) |
| Secret | signKey, the shared signing key |
| Calculation | sign = Base64(HMAC-SHA256(signKey, requestBody)) |
| Transport | HTTP request header xmh-hmc-signature |
The platform only treats HTTP status 200 as success. The errCode in the merchant response body is not parsed. Non-200 responses are treated as failures and retried on a schedule.
| Item | Rule |
|---|
| QPS per instance | 50 |
| Scaling | Horizontal |
| Production instances | 20+ instances for high-traffic endpoints |
This section covers outbound (merchant → Xcotton) endpoints only. For webhooks see §4.3.
A call is successful only when both conditions hold:
| Condition | Value |
|---|
| HTTP status | 200 (business failures still return HTTP 200 — check errCode) |
| Business code | errCode = 0 |
| Item | Rule |
|---|
| Retryable | The 920xxx family in the error-code appendix (timeouts, rate limits, transient failures) |
| Do not retry blindly | Parameter errors, authentication failures, business rejections and other non-920xxx codes — repeating the call is usually pointless |
| Idempotency | For write endpoints such as cancellation, carry a business idempotency key (e.g. cancelId); do not assume every endpoint tolerates blind retries |