Полная справка по API: https://api.mailoo.app/docs/v1
Используйте интеграцию Market для управления минимальным каталогом товаров в панели управления Mailoo и предоставления JSON только для чтения вашей витрине или BFF. Каждый внешний маршрут требует ``X-API-Key``; анонимного доступа нет.
Для массового редактирования через CSV (экспорт → редактирование → загрузка → предпросмотр/применение) см. market-catalog-csv{.interpreted-text role="doc"}.
Обзор
- Авторизация: Заголовок ``X-API-Key`` в каждом запросе.
- Область действия: Ключи RESTRICTED требуют ``market.external-read`` в
allowedOperations(ключи FULL тоже подходят). - Маршрутизация:
GET {baseUrl}/api/v1/market/{projectUid}/integrations/{integrationId}/…--- тот же формат пути, что и у ресурсов панели управления в/projects/{uid}/integrations/{id}/market/…, без префиксаprojects. - Владение: API-ключ должен принадлежать владельцу проекта;
projectUidиintegrationIdдолжны ссылаться на ACTIVE интеграцию типа MARKET. - Видимость: Товары имеют статус
DRAFT/PUBLISHED/ARCHIVED. Внешний API списка/детали/вариантов возвращает только товары со статусомPUBLISHED. API панели управления по-прежнему доступен для всех статусов владельцам. - IndexNOW: Опциональный
config.indexNowна интеграции Market --- Mailoo отправляет POST с идентификаторами товаров на ваш URL уведомлений при публикации/изменении; вы отправляете URL в IndexNOW (indexnow-notify{.interpreted-text role="doc"}). Строки контента содержатindexNowStatus. - Поля товара (панель управления): Каноническое описание товара и варианта хранятся в
attributes; опциональные наложения по языкам --- вlocales.<code>(nameиattributesдля каждой записи).name--- атрибут базового типа в записях товара;sku--- обязательный атрибут базового типа, отмеченный как ось варианта (хранится на вариантах, используется при поискеeffective-price). - JSON товара (только внешний список/деталь/варианты): Ответы не содержат объект
locales.attributesтовара и каждого варианта уже объединены для одного языка: базовые значения из записи панели управления плюс наложение для разрешённой локали (nameпо локали отображается на ключ атрибутаname). Разрешённая локаль = опциональный параметрlocale=<code>если задан и валиден (например,en,de,pt-br); иначе ``defaultCatalogLocale`` изconfigинтеграции MARKET (задаётся на экране Редактировать интеграцию в панели управления); если не задан --- ``en``. Невалиднаяlocaleвозвращает 400. - Поля товара (внешние): После объединения те же правила, что и в панели управления, применяются к плоским
attributes(включая семантикуname/skuвыше). - Формат ID: Идентификаторы ресурсов MARKET в параметрах запроса и полезных нагрузках ответов принимают и возвращают значения
cuidиcuid2(например, id тегов/товаров/прайс-листов/поставщиков). - Типы цен: См. Типы цен и итоговая цена ниже. Для выбора прайс-листа интеграторы должны настроить и отправить ключ типа цены (параметр ``kind`` в ``GET .../effective-price``). Id типа цены (CUID) --- внутренний (присутствует в некоторых JSON); он не является основным внешним идентификатором.
- Флаг корневого элемента интегратора: Объекты тегов содержат
useAsRoot. Интеграторы могут использовать теги сuseAsRoot = trueкак корневые элементы каталога верхнего уровня (допускается несколько тегов), даже еслиparentTagIdне null.
Иконки тегов: Объекты тегов могут содержать iconMediaId. Для получения байтов изображения используйте интеграцию Images с ключом, допускающим ``image.external-read`` (см. images{.interpreted-text role="doc"}).
Примечание к отображению в панели управления Mailoo: useAsRoot предназначен для логики меню внешнего интегратора. Отображение и порядок дерева тегов в панели управления Mailoo остаются прежними и следуют существующим правилам иерархии.
Переменные окружения BFF
Совпадают с панелью Подключение на странице интеграции (те же имена). Типичные серверные переменные:
MAILOO_MARKET_API--- базовый URL API (например,https://api.mailoo.appили ваш dev-хост), без завершающего слэша.MAILOO_MARKET_API_KEY--- API-ключ с ``market.external-read`` (или FULL).MAILOO_MARKET_PROJECT_UID--- UID проекта.MAILOO_MARKET_INTEGRATION_ID--- Id интеграции Market (CUID).
Эндпоинты (все GET, все требуют X-API-Key)
Замените {baseUrl}, {projectUid}, {integrationId} и параметры пути по необходимости.
GET {baseUrl}/api/v1/market/{projectUid}/integrations/{integrationId}/types--- Типы товаров и атрибуты.GET …/products--- Все опубликованные товары (варианты и привязки к тегам включены; объединённая локаль, безlocales). Опциональный параметр ``locale`` (см. Обзор).GET …/products/{productId}--- Один товар (тот же контракт).GET …/products/{productId}/variants--- Только варианты; каждая строка имеет объединённыеattributesи не содержитlocales. Опциональный ``locale``.GET …/products/{productId}/price-range--- Для одного опубликованного товара: итоговая цена за единицу по вариантам (minAmount/maxAmountсовпадают для одного типа цены) плюс опциональная товарная строка и агрегат мин/макс по этим числам. Параметры: ``kind`` или ``priceKindId`` (те же правила, что и у ``effective-price``), опциональные ``measurementUnitId``, ``at`` (ISO datetime), опциональный ``currency`` (ISO 4217, три буквы). Разрешение валюты: если ``currency`` не указан, валюта ответа берётся из первой подходящей строки прайса при обходе активных листов в хронологическом порядке (``effectiveAt`` по возрастанию) и строк по порядку ``id``; для слияний используются только строки в этой валюте (другие валюты игнорируются). Если ``currency`` указан, участвуют только строки в этой валюте; поле ``currency`` ответа дублирует запрос даже при отсутствии подходящих строк (суммы будут null).GET …/tagsиGET …/tags/{tagId}--- Теги каталога.GET …/price-listsиGET …/price-lists/{priceListId}--- Внутренние прайс-листы.GET …/effective-price--- Определение цены за единицу для товара и типа цены. Используйте параметр ``kind=<priceKindKey>`` (см. Типы цен и итоговая цена). Опциональные:productId/variantId/measurementUnitId/at(ISO datetime). (Параметр ``priceKindId`` принимается для внутренних вызовов или BFF; внешний код каталога должен использовать ``kind``.)GET …/suppliers--- Поставщики.GET …/suppliers/{supplierId}/products--- Товары поставщика.GET …/suppliers/{supplierId}/price-listsиGET …/suppliers/{supplierId}/price-lists/{supplierPriceListId}--- Прайс-листы поставщика.GET …/units--- Единицы измерения.GET …/packagings--- Упаковки.GET …/packaging-equivalences--- Эквиваленты упаковок.
Ответы используют { success: true, data: … }. Для внешних списка/детали/вариантов товаров data соответствует форме с объединённой локалью выше (без locales). Аутентифицированный GET /api/v1/projects/…/market/products… панели управления по-прежнему возвращает полную форму редактора, включая locales.
Типы цен и итоговая цена
- В базе данных тип цены имеет стабильный строковый ``key`` (уникальный для интеграции MARKET, например,
base) и внутренний ``id`` (CUID). Ключ --- это то, что следует настроить в окружении / CMS («используйтеbaseдля прайс-листов»). - Получение цены из внешнего или витринного кода: вызовите ``GET {baseUrl}/api/v1/market/{projectUid}/integrations/{integrationId}/effective-price`` с ``kind=<key>`` и ``productId=`` (и опциональными ``variantId``, ``measurementUnitId``, ``at``). Это поддерживаемый контракт: ``kind`` --- ключ типа цены, а не CUID. Все активные прайс-листы с
effectiveAt <= atобъединяются в хронологическом порядке (старые сначала, новые накладываются): для заданного ``variantId`` выигрывает новейшая строка для конкретного варианта по SKU; если ни один лист после не переопределяет этот SKU, старая строка остаётся. Если строки по SKU нет вообще, применяется новейшая товарная строка (``variantId`` null). Если ``variantId`` не указан, учитываются только товарные строки (выигрывает новейшая). - ``priceKindId`` в строке запроса предназначен только для вызывающих, которые уже хранят CUID (инструменты Mailoo, BFF, зеркалирующие внутренние id). Не используйте параметр
priceKindIdкак основной внешний API для выбора прайс-листа --- используйте ``kind``. - Ответы могут содержать ``priceKindId`` и ``priceKindKey`` (и аналогичные у строк прайс-листа). Вы можете читать ``priceKindKey``; ``priceKindId`` в ответах --- для корреляции и не предназначен для жёсткого кодирования в публичном фронтенде.
- Жизненный цикл прайс-листа (пути чтения):
GET …/price-listsи полезные нагрузки списка включают ``archivedAt`` и ``endsAt`` при наличии. ``GET .../effective-price``, ``GET .../products/{productId}/price-range`` и ценообразование при оформлении заказа учитывают только листы, которые не архивированы и, если ``endsAt`` задан, только когда момент ``at`` запроса строго раньше ``endsAt``. Вызовы панели управления могут PATCH / DELETE внутренние прайс-листы через ``/api/v1/projects/.../market/price-lists/{priceListId}`` (Bearer); удаление отклоняется с 409, если измерения какой-либо строки фигурируют в существующей строке заказа для этой интеграции.
Типы значений атрибутов товара (/types)
GET …/types возвращает атрибуты типов с:
valueType:STRING,NUMBER,BOOLEAN,ENUM,SELECT,MD_TEXToptions:string[] | null
SELECT --- строгий списочный тип. Для SELECT options --- непустой список допустимых значений, и значения атрибутов товара/варианта должны точно совпадать с одним из них.
``MD_TEXT`` и изображения: Любой атрибут товара или варианта с valueType: MD_TEXT может содержать встроенные изображения Mailoo в синтаксисе markdown (например, ). При записи панель управления нормализует URL изображений Mailoo в этих полях; JSON товаров и вариантов из ответов GET также нормализует вложенные строки, чтобы интеграторы видели каноническую форму публичного встраивания (…/api/v1/images/public/content?id=<mediaId>). Для получения байтов изображения по этому URL используйте API Images с ключом, допускающим ``image.external-read`` (см. images{.interpreted-text role="doc"}).
``MD_TEXT`` и HTML (только внешний GET каталога): На внешних маршрутах GET …/products, GET …/products/{productId} и GET …/products/{productId}/variants (с X-API-Key и опубликованным товаром) API добавляет параллельное строковое поле <attributeKey>Html рядом с каждым Markdown-значением для атрибутов, объявленных как MD_TEXT на любом из связанных типов товара, в объединённых attributes товара и каждого варианта (отдельного дерева locales в ответе нет). Используйте оригинальный ключ для исходного Markdown (как content блога) и …Html для серверного HTML (как htmlContent блога). Ответы GET /projects/…/market/… панели управления не содержат эти поля *Html, чтобы не создавать лишнюю нагрузку при редактировании.
Галерея и изображение для списка: Каждый товар может иметь упорядоченный массив ``images`` (объекты с id, mediaId, sortOrder и каноническим ``url``) плюс ``previewMediaId`` и ``previewImageUrl``. Превью --- изображение, рекомендуемое для карточек каталога / списков товаров; если ``previewMediaId`` равен null, интеграторы могут использовать первое изображение галереи. ID медиа галереи валидируются при создании/обновлении (READY медиа, в контексте USER / PROJECT / INTEGRATION владельца для этого каталога). Опциональное масштабирование на лету: добавьте ``w``, ``h``, ``fit``, ``format``, ``q`` к URL публичного контента, как документировано в images{.interpreted-text role="doc"}.
Пример определения атрибута:
{
"key": "display_type",
"name": "Display Type",
"valueType": "SELECT",
"options": ["LCD", "OLED", "micro-OLED", "QD-LCD"]
}
Примечание о несовместимом изменении: интеграции, ранее использовавшие ENUM как свободную строку, должны перейти на SELECT, если требуется строгий список допустимых значений.
Панель управления: создание товаров из JSON
В панели управления MARKET (раздел Товары) можно подготовить полезные нагрузки товаров в виде JSON:
- Скопировать шаблон --- копирует текущий черновик товара + подсказки схемы:
schemaVersionproduct(typeIds,attributes,tagIds)template.attributeSchema(ключи, типы значений, обязательность, опции SELECT)template.availableTags(все доступные теги каталога)- Вставить данные товара --- переключает форму в режим ввода JSON, куда вы вставляете полезную нагрузку вручную (запрос на чтение буфера обмена не требуется).
- Создать товар (в режиме JSON) валидирует полезную нагрузку на сервере и создаёт товар при успешной валидации.
Для массового обновления предпочтительнее загрузка/выгрузка CSV: market-catalog-csv{.interpreted-text role="doc"}.
Ожидаемый формат JSON
{
"schemaVersion": 1,
"product": {
"typeIds": ["<typeId-1>", "<typeId-2>"],
"attributes": {
"name": "Vision Pro X Ultra",
"display-type": "micro-OLED",
"short_description": "Flagship model.",
"full_description": "## Vision Pro X Ultra"
},
"tagIds": ["<tagId-1>"]
}
}
Поведение валидации
Серверная валидация --- единственный источник истины (дублирование доменных правил на клиенте не требуется):
- Панель управления вызывает
POST /api/v1/projects/{uid}/integrations/{id}/market/products/validateперед созданием. - Эндпоинт использует те же правила валидации, что и создание товара:
- неизвестные ключи атрибутов отклоняются
- обязательные атрибуты должны присутствовать
- типы значений должны совпадать (NUMBER/BOOLEAN/STRING/MD_TEXT/ENUM/SELECT)
- значения
SELECTдолжны быть одним из объявленныхoptions - id тегов должны принадлежать интеграции
- При неуспешной валидации товар не создаётся и отображается сообщение об ошибке API.
Оформление заказа по e-mail (заказы)
Вышеописанный API чтения каталога не включает корзину: витрина интегратора хранит состояние корзины (например, localStorage) и отправляет снимок при оформлении заказа покупателем.
- Создание заказа (сервер / BFF):
POST {baseUrl}/api/v1/market/{projectUid}/integrations/{integrationId}/ordersс заголовком ``X-API-Key`` и областью ``market.order.submit`` (ключи RESTRICTED) или FULL. Тело запроса: e-mail покупателя ``customerEmail``, ``items[]`` (каждая строка: ``productId``, ``quantity`` как десятичная строка, и либо ``priceKindKey`` (ключ интегратора, напримерbase), либо ``priceKindId`` (CUID) --- не оба, опциональные ``variantId`` / ``measurementUnitId``). Mailoo определяет цены за единицу из тех же прайс-листов, что и ``GET .../effective-price``; ответ включает ``accessToken`` и ``accessExpiresAt`` (временный доступ к заказу только для чтения). - Чтение заказа (браузер / любой клиент):
GET {baseUrl}/api/v1/market/public/orders/{orderId}?token=или заголовок ``X-Market-Order-Token`` --- без API-ключа. Возвращает снимок заказа, пока токен действителен (TTL по умолчанию 72 часа, переопределяемый через ``MARKET_ORDER_ACCESS_TOKEN_TTL_HOURS`` на хосте API). Отсутствующий/невалидный/просроченный токен возвращает 404 (то же сообщение, что и при неверном id). - Панель владельца (JWT): список/получение/обновление заказов через
/api/v1/projects/{uid}/integrations/{id}/market/orders/…(Bearer).
Полный поток, паттерны BFF и операционные заметки: market-order-email-checkout{.interpreted-text role="doc"}.
Дополнительные материалы
- OpenAPI:
https://api.mailoo.app/docs/v1 - CSV-процесс в панели управления:
market-catalog-csv{.interpreted-text role="doc"}