Практическое руководство API v2
1. Сервер, токен и первый запрос
Базовый URL — адрес вашей установки NWMS без /api/v2. Документация localhost описывает локальный код; доступность нового метода на другом сервере зависит от установленного выпуска. Используйте Bearer-токен своей учётной записи. Права и область данных пользователя сохраняются при обращении через API.
export NWMS_BASE_URL='https://YOUR-NWMS-HOST'
export NWMS_TOKEN='YOUR-TOKEN'
export NWMS_REQUEST_KEY='UNIQUE-KEY-FOR-ONE-OPERATION'
curl --globoff "${NWMS_BASE_URL}/api/v2/products?per_page=1" \
--header "Authorization: Bearer ${NWMS_TOKEN}" \
--header 'Accept: application/json'2. Структура запроса и ответа
| JSON | Передавайте Content-Type: application/json. Изменяемые публичные поля находятся внутри data. Вложенные строки, массивы товаров и команды имеют собственные схемы — сверяйте конкретный метод. |
|---|---|
| GET item | {"data": {...}, "meta": {"api_version": "v2"}} |
| GET collection | {"data": [...], "links": {...}, "meta": {...}} |
| PUT / PATCH | Не считайте PUT полной заменой автоматически. Для шаблонов уведомлений оба метода изменяют только переданные поля. Пропуск поля и явный null имеют разное значение; nullable указан в таблице схемы. |
| IDs | ID документа, строки, склада, партии и уникального SSCC обозначают разные сущности. Не подставляйте внешний ID маркетплейса вместо внутреннего WMS ID. |
3. Фильтры, сортировка и выгрузка
Допустимые фильтры перечислены у каждого endpoint. Типовой формат — filter[field]; сортировка sort=-updated_at,id задаёт убывание даты и возрастание ID. include включает только разрешённые связи. Для уведомлений per_page по умолчанию 50, максимум 250; другие методы могут иметь иной предел. Передавайте query-параметры через URL-кодирование, особенно кириллицу, плюс в часовом поясе и квадратные скобки.
curl --get "${NWMS_BASE_URL}/api/v2/notification-templates" \
--header "Authorization: Bearer ${NWMS_TOKEN}" \
--header 'Accept: application/json' \
--data-urlencode 'filter[status_id]=1' \
--data-urlencode 'filter[name]=Приёмка' \
--data-urlencode 'include=bindings' \
--data-urlencode 'sort=-updated_at,id' \
--data-urlencode 'per_page=50' \
--data-urlencode 'page=1'При выгрузке сохраняйте одинаковые фильтры и сортировку на всех страницах, переходите по links.next до null. Изменяющаяся база не является снимком: храните ID и updated_at, устраняйте повторения на своей стороне. after_id используйте только там, где он явно документирован. Для created_at/updated_at граница YYYY-MM-DD включает весь день в часовом поясе приложения; явно согласуйте часовой пояс сервера при синхронизации.
4. Идемпотентность и восстановление после таймаута
Для обычных изменяющих v2-операций с этим middleware Idempotency-Key необязателен, но рекомендуется; максимум 191 символ. Успешный ответ хранится сутки в области пользователя, HTTP-метода и пути. Повтор должен содержать тот же URL и те же байты тела: даже другая сериализация одинакового JSON может дать конфликт. Ответ повтора содержит Idempotency-Replayed: true. Ошибочные ответы не сохраняются как успешный результат.
| IDEMPOTENCY_KEY_CONFLICT / 409 | Ключ уже использован с другим URL/телом. Для новой логической операции создайте новый ключ; при повторе восстановите исходный запрос. |
|---|---|
| IDEMPOTENCY_KEY_IN_PROGRESS / 409 | Предыдущая операция ещё выполняется. Дождитесь результата; не создавайте дубликат с новым ключом. |
| Cross-dock | Idempotency-Key обязателен, включая создание приёмки type_id=3. Командный контракт дополнительно требует UUID command_id и для версионируемых ресурсов актуальную version. Подробности — кросс-докинг. |
5. Обработка ошибок
| 401 | Проверьте токен и заголовок Authorization. |
|---|---|
| 403 | Проверьте разрешение на модуль и действие; смена ID не даёт дополнительных прав. |
| 404 | Объект отсутствует или не входит в область видимости. Проверьте домен, ID родителя и вложенного объекта. |
| 422 | Исправьте поля из errors. Автоматический повтор неизменного тела обычно бесполезен. |
| 409 | Разберите код конфликта. Для устаревшей version перечитайте ресурс; для выполняющейся команды дождитесь результата. |
| 429 | Снизьте частоту и учитывайте Retry-After, если сервер его вернул. Используйте ограниченное число повторов с растущей задержкой. |
| 5xx / timeout | Результат записи может быть неизвестен. Сначала сверяйте ресурс и повторяйте исходный запрос с тем же ключом. Сохраняйте время, метод, путь, HTTP-код и тело ответа без токена. |
Не полагайтесь на один универсальный error: cross-dock использует meta.error_code, а обычный конфликт ключа — error. Точная схема и примеры приведены у каждой операции.
Документы, партии и фактические остатки
| stocks.physical / stocks.aggregate | Массивы складских итогов: warehouse_id, warehouse_name, stockTotal, stockReserved, stockAvailable. stocks.available_by_warehouse — объект с ключами ID складов. Не воспринимайте эти три поля как одинаковую структуру. |
|---|---|
| GET /api/v2/products/{id}/stock-lots | Физически размещённый товар по партиям и датам. Резерв без конкретной партии нельзя вычитать из произвольно выбранной партии. Сверяйте единицы количества и склад в ответе. |
| accepted_lots | Фактические партии приёмки. Ожидание, фактическая приёмка и размещение — разные показатели. Их расхождение само по себе не означает ошибку API. |
| requested_lot / fulfillment_lots | В строке заказа requested_lot описывает требование, fulfillment_lots — фактический подбор; picked_quantity — подобранное количество. Несколько фактических партий нельзя сводить к одной дате. |
| movement_type / operation | Подбор может давать пару снятия резерва и физического списания. Различайте operation; не считайте обе строки двумя списаниями. Перемещения объединяются через movement_group_id; transfer_kind уточняет вид. |
| GET /api/v2/closed-boxes | Реальные закрытые короба с уникальным SSCC: balance=1, balance_unit=box. Штрихкод типа упаковки из products не является уникальным SSCC. Открытые, отгруженные и только ожидаемые короба не входят в текущий закрытый остаток. |
| acceptance_date | При создании обычной/возвратной приёмки (type_id=1/2) текущий обработчик ставит серверное now(), даже если передана acceptance_date. Изменение существующей приёмки принимает дату. Создание type_id=3 использует отдельный cross-dock сервис. created_at/updated_at — технические метки, не дата документа. |
| Lifecycle | Отмена приёмки — отдельная команда cancel с проверкой состояния; она не равна удалению. Для строк используйте /lines, а устаревшие /offers сохраняются для совместимости. Подробные ограничения смотрите у операции. |