Skip to main content

API basics

This section is for the developer who connects an accounting or ERP system to the WMS. It describes how your code signs in, which headers every request carries, and how to read the answers. The pages that follow cover each exchange operation.

The WMS is a cloud service: every company works with the same service at https://app.wms.com.ua, and all requests in this section go to that address. Before you start, an administrator of your company sets up the WMS side and gives you three things — see the hand-off checklist:

  • the company code (on the Client sign-in screen it is the Tenant field);
  • the technical token — a long-lived secret of the technical user;
  • the external system code — the code under which your system is registered.

API description​

The service publishes a machine-readable description of its whole API. The integration endpoints are listed under the tags that start with Integrations. The description is published on the test environment:

Signing in​

Your code does not send the technical token with every request. It exchanges the token for a short-lived access token and uses that instead:

POST /api/v1/auth/technical/jwt
Authorization: Bearer <technical token>
WMS-Tenant-Code: <company code>

The request has no body. The answer:

{
"accessToken": "eyJhbGciOi…",
"expiresAt": "2026-10-27T10:15:00.123Z"
}
  • An access token lives for about 10 minutes. Keep it and reuse it until shortly before expiresAt, then sign in again.
  • A technical user can hold only a few access tokens at the same time. Signing in on every request pushes out the tokens of your other running processes, so cache the token.
  • If a request gets 401, the access token has expired or was revoked: sign in again and repeat the request once. If the sign-in itself gets 401, the technical token is wrong, expired or was replaced — ask the administrator for a new one (Issuing a token).

Request headers​

HeaderWhenWhat it carries
Authorization: Bearer <access token>every requestthe access token from the sign-in
WMS-Tenant-Codeevery requestthe company code
Content-Type: application/jsonrequests with a bodyall bodies are JSON in UTF-8
WMS-Idempotency-Keyimport requestsa unique key of this request, so that repeating it is safe; see Import
Accept-Languageoptionalthe language of error messages, for example uk or en
WMS-Correlation-Idoptionalyour own request id; quote it when you report a problem

The client used in the examples​

Every example in this section builds on the small client below. It signs in, caches the access token, adds the headers and repeats a request once after a 401. The JavaScript and Python versions read the connection settings from environment variables (WMS_BASE_URL, WMS_TENANT, WMS_TOKEN, WMS_EXTERNAL_SYSTEM); the 1C version keeps them in a structure created by НовоеПодключение. Never put the technical token into source code.

// WMS integration client: exchanges the technical token for a short-lived
// access token (JWT) and sends authorized requests. Node.js 18+, no dependencies.
const config = {
baseUrl: process.env.WMS_BASE_URL, // e.g. https://app.wms.com.ua
tenant: process.env.WMS_TENANT, // company code
token: process.env.WMS_TOKEN, // technical token issued in the WMS Client
externalSystem: process.env.WMS_EXTERNAL_SYSTEM, // external system code
};

let jwt = null; // {accessToken, renewAt}

async function getAccessToken() {
if (jwt && Date.now() < jwt.renewAt) return jwt.accessToken;
const res = await fetch(`${config.baseUrl}/api/v1/auth/technical/jwt`, {
method: 'POST',
headers: {Authorization: `Bearer ${config.token}`, 'WMS-Tenant-Code': config.tenant},
});
if (!res.ok) throw new Error(`WMS sign-in failed: HTTP ${res.status} ${await res.text()}`);
const {accessToken, expiresAt} = await res.json();
// Renew 30 seconds before the token expires.
jwt = {accessToken, renewAt: Date.parse(expiresAt) - 30_000};
return accessToken;
}

class WmsError extends Error {
constructor(status, body) {
const details = body?.errors?.map((e) => `${e.path || '(request)'}: ${e.message}`).join('; ');
super(`WMS HTTP ${status}: ${body?.message ?? body}${details ? ` — ${details}` : ''}`);
this.status = status;
this.body = body;
}
}

async function wmsRequest(method, path, {query = {}, body, idempotencyKey} = {}) {
const url = new URL(config.baseUrl.replace(/\/$/, '') + path);
for (const [name, value] of Object.entries(query)) {
// Arrays become repeated parameters: entityTypes=A&entityTypes=B
for (const item of [].concat(value ?? [])) url.searchParams.append(name, String(item));
}
const send = async () => {
const headers = {
Authorization: `Bearer ${await getAccessToken()}`,
'WMS-Tenant-Code': config.tenant,
Accept: 'application/json',
};
if (body !== undefined) headers['Content-Type'] = 'application/json';
if (idempotencyKey) headers['WMS-Idempotency-Key'] = idempotencyKey;
return fetch(url, {method, headers, body: body === undefined ? undefined : JSON.stringify(body)});
};

let res = await send();
if (res.status === 401) {
// The access token expired or was revoked: sign in again and repeat once.
jwt = null;
res = await send();
}
const text = await res.text();
let data = text;
try {
data = text ? JSON.parse(text) : null;
} catch {
// Some errors come back as plain text.
}
if (!res.ok) throw new WmsError(res.status, data);
return {status: res.status, headers: res.headers, body: data};
}

Errors​

A request that the server rejects as a whole comes back with a 4xx status. Most errors carry a JSON body:

{
"message": "INVALID_DATA_FORMAT",
"errors": [
{"path": "code", "message": "External system with code NOPE not found"}
]
}

path points at the field that caused the problem (an empty path means the request as a whole). Some errors come back as plain text instead, for example Limit must be between 1 and 500, so do not assume the body is always JSON. The error texts follow the Accept-Language header.

StatusMeaningWhat to do
400The request is invalid: a wrong field, a value out of range, an unknown external system code.Fix the request; repeating it unchanged gives the same answer.
401The access token is missing, expired or revoked.Sign in again and repeat once.
403Access is denied: the external system is switched off or lacks a capability, the company code does not match, or the licence has no free technical seats.Ask the administrator to check the external system and the technical user.
404The address does not exist.Check the path.
429Too many requests in a short time.Wait (the answer says how long) and repeat.
5xxA failure on the server side.Wait and repeat; for an import, repeat with the same idempotency key.

An import is different: the server answers 200 even when some of the items fail, and reports each item separately; see Import.