Мерксалис

Мерксалис — API интеграций v1

Мерксалис — облачная платформа для управления товарными и складскими процессами в электронной торговле.

API интеграций предназначен для обмена данными между Мерксалис и внешними учётными, складскими и аналитическими системами. Внешняя система может передавать каталог, коды и штрихкоды товаров, а также получать нормализованные операционные данные Мерксалис.

Базовый адрес https://merxalis.ru/integration/v1 Протокол HTTPS Формат JSON, UTF-8 Версия v1 OpenAPI /integration/openapi.json

Быстрый старт

  1. Создайте API-ключ в разделе Настройки → API-ключи. Открытое значение ключа показывается один раз.
  2. Передавайте ключ в заголовке Authorization: Bearer mrx_v1_....
  3. Проверьте контекст запросом GET /context. Организация, права ключа и действующие лимиты определяются сервером по API-ключу.
  4. Для передачи каталога используйте POST /catalog/products/upsert.
  5. Для получения операционных данных используйте GET /operations.

API-ключи

API-ключ является только средством авторизации и проверки прав доступа. Каталог и операционные данные принадлежат организации, а не конкретному API-ключу.

Организация может создавать необходимое ей количество API-ключей и использовать их в своих системах по собственным правилам. Максимальный срок действия ключа — 365 дней.

Для записи каталога требуется право catalog:write, для чтения каталога — catalog:read, для чтения операционных данных — operations:read.

Каталог товаров

Каталог Мерксалис хранит канонический товар организации. Все идентификаторы передаются строками, поэтому ведущие нули сохраняются.

Минимальный пакет:

{
  "items": [
    {
      "client_ref": "row-00125",
      "internal_code": "00125",
      "name": "Фартук кухонный",
      "barcodes": [
        "4673752160115",
        "4601234567890"
      ]
    }
  ]
}

Дополнительные идентификаторы

Для внешнего стабильного идентификатора используйте массив identifiers. Его элементы содержат type, необязательный namespace, value и признак resolvable.

{
  "type": "external_id",
  "namespace": "1c",
  "value": "6fd31a5e-1fb8-11ef-9f6a-0242ac120002",
  "resolvable": false
}

internal_code и штрихкоды используются как складские идентификаторы товара. Для дополнительного идентификатора resolvable=false является значением по умолчанию.

Одно распознаваемое нормализованное значение внутри организации может вести только к одному каноническому товару. Неоднозначность между разными товарами возвращается как 409 RESOLUTION_CONFLICT.

Массовая запись

POST /catalog/products/upsert принимает до 1000 строк. Результат каждой строки возвращается отдельно. client_ref предназначен только для сопоставления строки запроса с результатом и не является идентификатором товара.

Обычный массовый upsert имеет merge-семантику: отсутствие ранее записанного штрихкода, дополнительного идентификатора или связи с маркетплейсом в новом пакете само по себе не удаляет существующее значение.

Связи с маркетплейсами

Для кабинета используется стабильный account_id, полученный через GET /marketplace-accounts. Идентификаторы товара на площадке передаются массивом external_identifiers.

Например, адаптер Wildberries использует типы nm_id и chrt_id. Общая модель не привязана к Wildberries и допускает другие наборы идентификаторов для Ozon, Яндекс Маркета и будущих площадок.

Методы каталога изменяют данные каталога Мерксалис и сами по себе не выполняют запись в API маркетплейса.

Полная синхронизация каталога

Полная синхронизация является полным снимком каталога организации. Она не принадлежит конкретному API-ключу.

  1. Откройте синхронизацию через POST /catalog/sync-runs и передайте точное expected_items.
  2. Передайте пакеты в POST /catalog/products/upsert с полученным sync_id.
  3. После успешной передачи всего снимка вызовите POST /catalog/sync-runs/{sync_id}/complete с confirm_full_snapshot=true.
{
  "mode": "full",
  "expected_items": 50000
}
{
  "confirm_full_snapshot": true
}

expected_items — количество уникальных канонических товаров полного снимка. Повторная строка того же канонического товара не увеличивает received_unique_items.

Если фактически полученное количество уникальных товаров не совпадает с expected_items, завершение возвращает 409 FULL_SYNC_INCOMPLETE, а отсутствие товаров не применяется.

Одновременно в одной организации может выполняться только одна полная синхронизация. Любой API-ключ этой же организации с правом catalog:write может продолжить работу с тем же sync_id.

Если товар отсутствует в подтверждённом полном снимке, он может быть архивирован. Товар, который был создан или существенно изменён после начала снимка, защищён от ошибочного архивирования устаревшим снимком.

Если ранее автоматически архивированный полным снимком товар появляется снова, он восстанавливается с тем же product_id.

Чтение изменений каталога

Для гарантированной инкрементальной синхронизации каталога используйте GET /catalog/changes.

Пока page.next_cursor не пуст, передавайте его в следующий запрос. После последней страницы сохраните sync_cursor для следующего цикла.

Курсоры непрозрачны: их нельзя разбирать, изменять или формировать самостоятельно.

Операционные данные Мерксалис

GET /operations предназначен для передачи внешним системам нормализованных операционных данных электронной торговли и склада, сформированных из бизнес-модели Мерксалис.

Этот метод не является прокси к API маркетплейса и не зависит от структуры Excel-выгрузки.

Важно: cursor метода /operations предназначен только для последовательной пагинации результата. Он не является курсором изменений и не гарантирует выдачу только записей, изменившихся после предыдущего обращения.

Переданные поставки и их состав

Эти методы предназначены для внешней учётной, складской или аналитической системы организации. Они читают сохранённую историю Мерксалис и не выполняют новую передачу маркетплейсу.

Все методы раздела требуют право operations:read. Организация определяется сервером по API-ключу и не передаётся клиентом в query или path.

Что означает confirmed. confirmed — подтверждённый результат передачи в state machine Мерксалис. Это не утверждение об окончательной приёмке товара маркетплейсом и не команда выполнить бухгалтерское списание. Конкретное учётное действие определяет внешняя система организации.

GET /dispatches — список передач

Для подтверждённых фактов:

GET /dispatches?status=confirmed&limit=100

Параметр account принимает стабильный публичный UUID кабинета маркетплейса. Также доступны фильтры marketplace, fulfillment_unit, fulfillment_unit_type, shipment, prepared_from, prepared_to, confirmed_from и confirmed_to.

Временные границы включительны. Время передаётся в ISO 8601 с часовым поясом и нормализуется сервером к UTC. limit по умолчанию равен 100 и может быть от 1 до 500.

Список сортируется по prepared_at DESC, затем по dispatch_id DESC. Если page.next_cursor не пуст, его нужно передать в следующий запрос с теми же фильтрами. Курсор непрозрачен, связан с организацией и нормализованным набором фильтров.

GET /dispatches/{dispatch_id} — состав передачи

Метод возвращает конкретную передачу, зафиксированные строки её состава, сохранённые идентификаторы и события.

После seal строки состава и идентификаторы являются историческим снимком. Весь detail-ответ при этом не является неизменяемым: статус, времена reconciliation и массив событий могут дополняться по мере развития state machine.

В строке доступны внешний идентификатор заказа, количество, внутренний код товара, штрихкоды каталога и маркетплейса, идентификаторы маркетплейса, артикул продавца и стикер.

Экземплярные идентификаторы находятся в массиве identifiers. value_base64 позволяет восстановить точную сохранённую последовательность байтов, включая специальные байты DataMatrix, например GS (0x1D). value_raw возвращается только для корректного UTF-8; для невалидного UTF-8 он равен null. Наличие сохранённых байтов само по себе не означает валидность кода маркировки.

GET /dispatch-events — поток событий

Это tenant-local последовательный поток событий prepared, in_flight, uncertain, confirmed и rejected.

При отсутствии cursor start=beginning читает поток с начала, а start=now начинает с текущей позиции. Если cursor передан, позиция определяется им.

На промежуточной странице продолжайте чтение через page.next_cursor. После последней страницы сохраните sync_cursor и используйте его как cursor в следующем цикле.

Сначала надёжно сохраните результат обработки и только после этого продвигайте cursor. Повторное чтение события не должно приводить к повторному учётному действию для уже обработанного dispatch_id с событием confirmed.

Пример HTTP / cURL

curl \
  "https://merxalis.ru/integration/v1/dispatches?status=confirmed&limit=100" \
  -H "Authorization: Bearer mrx_v1_..."

Ошибки

400 — повреждённый cursor либо cursor другой выборки; 401 — недействительный API-ключ; 403 — отсутствует operations:read; 404 — передача не найдена или недоступна текущей организации; 422 — некорректные параметры, время без часового пояса или неверный временной диапазон.

Параллельные изменения и безопасные повторы

ETag / If-Match

Товар имеет числовую version. Одиночные изменяющие операции используют ETag и требуют If-Match с ранее прочитанной версией.

If-Match: "17"

Если ресурс уже изменён, сервер возвращает 412 VERSION_MISMATCH. Если обязательный заголовок отсутствует — 428 PRECONDITION_REQUIRED.

Idempotency-Key

Изменяющие POST поддерживают Idempotency-Key. Повтор того же запроса с тем же ключом возвращает прежний логический результат. Использование того же ключа для другого запроса возвращает 409 IDEMPOTENCY_KEY_REUSED.

Повтор запроса после временной ошибки

HTTP Действие
408, 429, 500, 502, 503, 504 Запрос можно повторить с увеличивающейся задержкой. Для операции записи сохраняйте тот же Idempotency-Key. Если сервер вернул Retry-After, он имеет приоритет.
400, 401, 403, 404, 409, 412, 422, 428 Сначала исправьте запрос, права или состояние ресурса. Автоматический повтор без изменения причины не требуется.

Примеры подключения

1С

Товар = Новый Структура;
Товар.Вставить("client_ref", "00125");
Товар.Вставить("internal_code", "00125");
Товар.Вставить("name", "Фартук кухонный");

Штрихкоды = Новый Массив;
Штрихкоды.Добавить("4673752160115");
Штрихкоды.Добавить("4601234567890");
Товар.Вставить("barcodes", Штрихкоды);

Идентификатор = Новый Структура;
Идентификатор.Вставить("type", "external_id");
Идентификатор.Вставить("namespace", "1c");
Идентификатор.Вставить(
    "value",
    "6fd31a5e-1fb8-11ef-9f6a-0242ac120002"
);

Идентификаторы = Новый Массив;
Идентификаторы.Добавить(Идентификатор);
Товар.Вставить("identifiers", Идентификаторы);

Товары = Новый Массив;
Товары.Добавить(Товар);

Тело = Новый Структура;
Тело.Вставить("items", Товары);

// POST https://merxalis.ru/integration/v1/catalog/products/upsert
// Authorization: Bearer mrx_v1_...
// Content-Type: application/json
// Idempotency-Key: Новый УникальныйИдентификатор()

Python

import uuid
import requests

token = "mrx_v1_..."

payload = {
    "items": [
        {
            "client_ref": "00125",
            "internal_code": "00125",
            "name": "Фартук кухонный",
            "barcodes": [
                "4673752160115",
                "4601234567890",
            ],
            "identifiers": [
                {
                    "type": "external_id",
                    "namespace": "erp",
                    "value": "erp-product-00125",
                    "resolvable": False,
                }
            ],
        }
    ]
}

response = requests.post(
    "https://merxalis.ru/integration/v1/catalog/products/upsert",
    headers={
        "Authorization": f"Bearer {token}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json=payload,
    timeout=30,
)

response.raise_for_status()
print(response.json())

HTTP / cURL

curl \
  -X POST \
  "https://merxalis.ru/integration/v1/catalog/products/upsert" \
  -H "Authorization: Bearer mrx_v1_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 8a91584c-470d-4f44-b985-2a3d16432e54" \
  -d '{
    "items": [
      {
        "client_ref": "00125",
        "internal_code": "00125",
        "barcodes": ["4673752160115"]
      }
    ]
  }'

Версионирование

Текущий контракт находится в пространстве /integration/v1. Совместимые расширения могут добавляться в v1. Несовместимое изменение публичного контракта требует новой основной версии, например /integration/v2.

Статический файл OpenAPI фиксируется только после прохождения предрелизной проверки фактического контракта.

История изменений

Версия Состояние
v1 PREPUBLIC Организационный каталог, простая модель API-ключей, массовая и полная синхронизация, курсор изменений каталога, связи с маркетплейсами, чтение операционных данных и историю передач с зафиксированным составом.