Vizen Shop

Vizen catalogue import via the API

/docs/catalog-importcurrentМашинный перевод с английского

Путеводитель для одного external разработчик или AI агент, заполняющий shop с products Использование персонального токена доступа (PAT).

Брат и сестра document /docs/webcoding покрытие page layout. Этот покрывает data: categories, products, images, attributes, variants связанный products. Полный schema: /openapi.json.

Это document Основан на обзоре реального import: "Каждая ошибка"" section Это ошибка, которая уже стоила кому-то переделки..

Тридцать секунд: что нужно знать before тот first запросить

ФактПоследствия
The shop определяется в key, Не является параметромcompany_id В запросе есть ignored; Другие жильцы невидимы
Цена является целым числом в shop currencyprice.currency правила: Каталог § 8; Несоответствие отклоняется
А. key Пишет либо в live site или в том, draftИмущество, которое key; нет header переключать его
category_id Это первичный category, categories[] есть дополнительный одинНе повторяйте первичный в categories[]
Галерея является отдельной shop resource, не product fieldСоздан в /galleries; products, categories Статьи ссылаются на него через gallery_ids[] (§7)
Списки требуют оба части аббревиатурыpage.limit без page.number → 400
Каталог живет без тот /v1 prefix/products, /categories; files и организация находится под /v1

1 шаг 0 — это key паспорт (обязательный))

GET /v1/account/token
Authorization: Bearer vz_pat_…
{
  "company_id": 7,
  "contour": "prod",
  "writes_to_live": true,
  "dev_header_allowed": false,
  "publish_allowed": false,
  "expires_at": null,
  "scopes": ["catalog:read", "catalog:write", "storage:read", "storage:write"],
  "store": {
    "name": "promo",
    "slug": "promo",
    "storefront_url": "https://promo.vizen.shop",
    "currency": "USD",
    "language": "ru",
    "custom_domain": ""
  },
  "capabilities": {
    "catalog_read": true, "catalog_write": true,
    "storage_read": true, "storage_write": true,
    "publish_dev": false, "store_settings_write": false
  },
  "warnings": [
    "Ключ пишет в ЖИВОЙ магазин: изменения видны посетителям сразу…",
    "Цена товара — целое число в валюте магазина (USD)…",
    "Сменить валюту, язык или название магазина этим ключом НЕЛЬЗЯ…"
  ]
}

Что с ним делать before Огромный текст:

  1. writes_to_live: true → Скажи owner явно: *"Посетители увидят, что немедленно"* — Ждать подтверждения.
  2. store.currency → compare с помощью price список currency. Если они отличаются, остановиться. The owner изменениях, currency в admin панель; а key cannot (capabilities.store_settings_write: false).
  3. capabilities → Проверьте, что required права, которые имеются. актуальный scope Это не означает, что метод открыт для key: тот capabilities Список вычисляется с фактической карты доступа, поэтому доверяйте ему..
  4. warnings → Передайте их в owner в full, без перефразирования.

ловушка. The contour когда-то был обнаружен судом: написать product и идти look Это больше не нужно и не должно быть done.


2. Пять правил, которые нарушают импорт чаще всего

2.1. shop currency Переопределяет тот, который вы посылаете

Цена является целым числом в shop currency: 6400 6400 единиц, а не центы. The price.currency договором, описанным в /docs/catalogue §8: a empty value Допускается; не-empty несоответствие возвратов 400 CURRENCY_IS_STORE_LEVEL. Проверь. price список currency против key паспорт before импортировать; там Не является конверсией.

2.2. Начало - обе части или ни одна

GET /products?page.limit=100              → 400 (Number must be >= 1)
GET /products?page.limit=100&page.number=1 → 200
GET /products                              → 200 (default page)

Если ты пройдешь page.limit, Вы также должны пройти page.number (нумерация начинается в 1).

2.3 Первичные и дополнительные categories различны fields

Полезначение
category_idпервичный category. Определяет product's main URL и его перечень
categories[]Дополнительный categories (ids). The product Также появляется в них
categories_replace: trueНужно только очистить: [] + Флаг удаляет все дополнительные category. Неполный список заменяет set с или без него

Ты не нужно Повторить первичный category в categories[] — это Создает избыточный link Вместо того, чтобы «укреплять» задание.

// create — primary only
POST /products   { "item": { "category_id": 85 } }
// create — primary + two additional
POST /products   { "item": { "category_id": 85, "categories": [86, 87] } }
// update — full replacement of additional ones
PUT /products/808 { "item": { "categories": [86, 87], "categories_replace": true } }
// update — clear additional ones, keep the primary
PUT /products/808 { "item": { "categories": [], "categories_replace": true } }

Обновление A Non-Pempty categories заменять дополнительное set — делает Не прибавлять к нему. category, читать product и отправить весь список (измерено 2026-10-28: [86, 87] затем {"categories":[88]} листья [88]). Ан Пустой массив без categories_replace означает «не меняйся»; с флагом это Проясняет set. На создание флага не требуется.

2.4 Флаги трех государств - это нити, а не булевы флаги

Некоторые переключатели inherit от shop, У них есть три страны и они приезжают в JSON как струна:

Полеценностизначение
catalog_hidden"" / "true" / "false""" и "false" — показанный; "true" — hidden из витрины магазина, поиска и подсчета разделов, карта по-прежнему открывается по своему адресу. PUT /products/{id} только: POST /products игнорировать его
comments_enabled, questions_enabled"" / "true" / "false"то же самое
reviews_scope"" / own / category / shop / off

is_published является регулярной булевый. Не смешивайте их: catalog_hidden: false (Булеан - это type error; Используйте струну "false", или "" перезагрузить.

Безопасная последовательность: создавать product с is_published: false → проверить читать → publish.

2.5. Category order это set Двигаясь, а не field

order В создании body это ignored: а new category always идти LAST Среди его братьев и сестер. value Вы читаете обратно, поэтому законно отличается от Тот, кого вы послали, не относитесь к этому как к error Не пытайтесь «заставить» number С другой стороны, писать.

Это держит положение брата и сестры плотным 0..n-1 set без дубликатов, которые before/after move Арифметика опирается на. field когда-то хранился как Отправлено, и каталог быстро накопился groups где каждый ребенок имел order = 0 — order Тогда было принято решение о внутренней id, и перетаскивание-drop из sections в admin панель перестала работать.

Если вам нужен конкретный order, set явно after Создавая все sections:

POST /categories/move
{ "from_id": "85", "to_id": "86", "type": "before" }   // before | after | inside

3. Каталог model

Shop (key)
├── Gallery (named media set; its own endpoint /galleries)
│      ↑ referenced by product, category, article and variant set
└── Category (storefront section; tree via parent_id)
    └── Product  ── primary category + additional ones
        ├── Attributes  (attribute values: material, country, …)
        ├── Variants within the product  (one card, switchers)
        └── Grouping membership          (several cards linked by axes)

Галерея не принадлежать в product: существует на его own, и тот же самый Галерея может быть размещена на нескольких products, а category и ан article сразу. Это то, что standard «Take the Gallery from Another» (Возьми галерею у другого) product" (§7).

Ан атрибут (/attributes) это shop-Широкий словарь: code, type, widget, список options. Используется как в качестве характеристики, так и в качестве переключателя. ось.


4. Варианты или связанные products: Как выбрать

Платформа поддерживает оба Выбор сделан один раз и дорого обходится изменение — compare Используя стол.

нуждаВарианты внутри a productГруппировка отдельных products
отдельно URL в комбинации✗ один URL + параметр✓ У каждого есть свой own
отдельно SEO титулов и description✗✓
Отдельная галерея для комбинации✓ set Ссылка на галерею (gallery_id, §5 шаг 4b)✓ У каждого есть свой own set из galleries
собственный SKU и price✓✓
Отдельные запасы level✓✓
отдельно card в каталожной сетке✗ один✓ каждый (extra Они являются hidden с catalog_hidden)
Стоимость установкиниже: 1 product + setsвыше: N products + а group

Выбор в одном line: только price, SKU и запасы различаются → variants; Фотографии тоже отличаются → variants + галерея на set; text, URL или position в Каталоговая сеть отличается → группирование.

"Другой цвет" может быть показан тремя способами, и они не заменяют друг друга.:

Если вам нужно…МеханизмГде это написаноСтоимость
другой чип и cart-line photo для variant, общий слайдset image image_idPUT /products/{id}/variantsодин image за set
Различные фотографии в слайдере когда variant это selected, один product, один URLгалерея + set gallery_idPOST /galleries → PUT /products/{id} → PUT /products/{id}/variants≤15 galleries на объект × ≤24 items; делиться SEO
каждый variant иметь его own URL, SEO, description и сетка cardproduct группированиеН. × POST /products + /product-groupsН. products поддерживать; extra cards hidden с catalog_hidden

image_id и gallery_id есть независимый set fields: тот first контроль чип и его cart line, Второй только слайдер.

Для промежуточных случаев (необходимы отдельные URL-адреса, но идентичные) content), использовать группировать: добавлять content Дешевле, потом разбиваешь product В девяти случаях не.


5 Сценарий А — один product с variants

Когда: Футболка в три раза sizes, Различаться только в price и запас.

# 1) Dictionary: axis attribute with options
POST /attributes
{ "item": {
    "code": "size", "name": "Размер",
    "data_type": "select",        # a switcher axis is always select
    "widget": "select",           # text | select | icon | swatch | photo
    "options": [ { "value": "s", "label": "S" },
                 { "value": "m", "label": "M" },
                 { "value": "l", "label": "L" } ] } }
# → result.id = 12, options have their own ids

# 2) Product
POST /products
{ "item": { "name": "Футболка TERRA", "category_id": 85,
            "price": { "price": 2400 }, "sku": "TERRA-TSHIRT",
            "is_published": false } }
# → result.id = 900

# 3) Product axes (replace-set)
PUT /products/900/variant-axes
{ "items": [ { "attribute_id": 12, "display": "text", "sort_order": 0 } ] }

# 4) Sets (replace-set): values = attribute_id → option value
#    is_active may be left out: a set without it is on (since 2026-10-28, as in the import);
#    "is_active": false switches one off
PUT /products/900/variants
{ "items": [
    { "values": { "12": "s" }, "price": 2400, "sku": "TERRA-TSHIRT-S", "is_active": true },
    { "values": { "12": "m" }, "price": 2400, "sku": "TERRA-TSHIRT-M", "is_active": true },
    { "values": { "12": "l" }, "price": 2600, "sku": "TERRA-TSHIRT-L", "is_active": true } ] }

# 4b) galleries and set binding (optional)
#     A gallery is a standalone resource: create it first, then reference it.
POST /galleries
{ "item": { "name": "Синие",
            "items": [ { "file": { "id": "<file_id_blue_1>" } },
                       { "file": { "id": "<file_id_blue_2>" } } ] } }
# → result.id = "31"   (gallery id is a STRING: int64 arrives as a string in JSON)

POST /galleries
{ "item": { "name": "Чёрные", "items": [ { "file": { "id": "<file_id_black_1>" } } ] } }
# → result.id = "32"

# product galleries are REFERENCES — the full set, in the desired order
PUT /products/900
{ "item": { "gallery_ids": ["31", "32"], "gallery_ids_replace": true } }

PUT /products/900/variants
{ "items": [
    { "values": { "12": "s" }, "price": 2400, "sku": "TERRA-TSHIRT-S",
      "gallery_id": "31", "is_active": true },
    { "values": { "12": "m" }, "price": 2400, "sku": "TERRA-TSHIRT-M", "is_active": true },
    { "values": { "12": "l" }, "price": 2600, "sku": "TERRA-TSHIRT-L", "is_active": true } ] }
# → variants_info.variants[].gallery_id in the GET /products/900 response confirms the binding

# 5) Verify and publish
GET  /products/900/variants
PUT  /products/900   { "item": { "is_published": true } }

подводные камни:

  • variant-axes и variants есть replace-набор: Отправьте полный комплект; все Недостающее удаляется.
  • Ось display более узкий: "" / text / icon / swatch. The value select является действительным для атрибута, но не для product ось.
  • Набор's price необязательно: пустой product's основание price используется.
  • Набор's is_active: Незамеченные средства на, здесь и в POST /products/import одинаково (с 2026-10-28); before, Набор, написанный здесь без сохранения, переключен off). false переключать его off.
  • Вариант без label дает пустую кнопку переключателя; характеристика's unit не добавляется к нему. Типы, этикетки, фильтры и шаблоны characteristics — GET /docs/characteristics.
  • Дубликат value комбинация отклоняется — сервер вычисляет заданную подпись.
  • Максимально: 20 осей, 500 множеств на product; до 10000 values (options) По характеристике — 10001-й с отказом 400 (с 2026 по 10-29; before, 200. Выше 200. options, В ОСНОВЕ ОСТИ ХАРОПАНИЯ перечислены только values тот product's наборы для использования.
  • gallery_ids в PUT /products/{id} это набор ссылок следующий categories_replace шаблон: непустая матрица → полная замена в order Пустая или незаполненная; field Опущенный → Обязательные нетронутыми остаются (безопасный копировать GET Тело вступает в PUT); пустой массив + "gallery_ids_replace": true → Галерея содержимое редактируется через их own конечная точка PUT /galleries/{id}, не через product.
  • Набор's gallery_id ссылки Живая галерея любой shop, не обязательно связан с этим product. Вот как ты "взяла галерею от другой product". Еще один арендатор's или несуществующий id → 404 GALLERY_NOT_FOUND. Если галерея будет удалена позже, набор не сломается: на читайте gallery_id возврат "0" И витрина показывает product's Первая галерея.
  • Одна галерея может сидеть на нескольких продуктах, а category И статья в The в то же время — GET /galleries/{id}/usage Показать полный список.
  • Набор изображения (image_id, Шаг 4) и галерея сетов (gallery_id) есть независимый поля: image_id Остается чип и тележка-линия preview, gallery_id Управляет только слайдером в магазине.

6. Сценарий В - отдельно products связанный между собой группировкой

Когда: пластина в 3 цветах × 3 sizes, Каждый из них со своим own фотографии и URL.

# 1) Two axis attributes
POST /attributes
{ "item": { "code": "color", "name": "Цвет", "data_type": "select",
            "widget": "swatch",
            "options": [ { "value": "light",  "label": "Светлый",   "color_hex": "#E8DCC8" },
                         { "value": "brown",  "label": "Коричневый","color_hex": "#8B5A3C" },
                         { "value": "black",  "label": "Чёрный",    "color_hex": "#2B2B2B" } ] } }
POST /attributes
{ "item": { "code": "diameter", "name": "Диаметр", "data_type": "select",
            "widget": "select", "unit": "см",
            "options": [ { "value": "20", "label": "20 см" }, { "value": "26", "label": "26 см" },
                         { "value": "32", "label": "32 см" } ] } }
# label is what the switcher button shows: without it the button is EMPTY, and unit is not added to buttons

# 2) Nine products — regular POST /products, each with its own slug, photos, price
#    (or one POST /products/batch, see §8)

# 3) Each product gets values on both axes (replace-set)
PUT /products/808/attributes
{ "items": [ { "attribute_id": 13, "value": "light" },
             { "attribute_id": 14, "value": "20"    } ] }

# 4) Group
POST /product-groups
{ "item": { "name": "Тарелка TERRA", "group_type": "variant_group" } }
# → result.id = 5

# 5) Group axes (replace-set) — order of switchers on the storefront
PUT /product-groups/5/axes
{ "items": [ { "attribute_id": 13, "display": "swatch", "sort_order": 0 },
             { "attribute_id": 14, "display": "text",   "sort_order": 1 } ] }

# 6) Members (replace-set)
PUT /product-groups/5/members
{ "items": [ { "product_id": 808, "sort_order": 0 },
             { "product_id": 809, "sort_order": 1 } ] }

# 7) Verify: the card returns the ready link
GET /products/808
# → group_info: axes, values, neighbour slugs, current member

подводные камни:

  • ось values есть set на product (PUT /products/{id}/attributes), не на group член: в members тот values field только для чтения.
  • Все трое PUTs заменить-set. Добавить десятую product в group — отправлять Все десять.
  • иметь один card В каталоге вместо девяти, set catalog_hidden: "true" На остальных - нет. drop вне группы — переключатель на card все еще leads к ним.
  • group_info в product Ответ является брат из result field, Не вложены в него. variants_info, attribute_sections и categories сидеть Там тоже.

7.Продукт. images

Загрузка занимает три steps (или один, если file уже в сети; подробности и Правила выращивания находятся в /docs/webcoding, §3. Сквозной "product + photo" Примером может служить §7.3..

POST /v1/storages/files
{ "section": "content", "file_type": "image", "original_name": "plate.jpg" }
# → result.id = <file_id>, upload.id, upload.url
PUT  <upload.url>                       # bytes, no Authorization
POST /v1/storages/files/<upload.id>/done  { }

# file already online — a single request
POST /v1/storages/files/from-url
{ "source_url": "https://…/plate.jpg", "section": "content", "file_type": "image" }

# the gallery is created SEPARATELY and once
POST /galleries
{ "item": { "name": "Основная",
            "items": [ { "file": { "id": "<file_id_2>" } },
                       { "file": { "id": "<file_id_3>" } } ] } }
# → result.id = "31"

# binding on CREATE — preview as an object, galleries as references
POST /products
{ "item": { "name": "Тарелка", "preview": { "id": "<file_id>" },
            "gallery_ids": ["31"] } }

# binding on UPDATE — main image as an id string
PUT /products/808
{ "item": { "preview_id": "<file_id>",
            "gallery_ids": ["31"], "gallery_ids_replace": true } }

подводные камни:

  • Создавать и обновлять принимать main image по-разному: в создании — preview: { "id": … }, Обновление — preview_id: "…" как струна. галереи В обоих случаях используют gallery_ids, массив из id Строки. Обновление preview: {id} также принимается как псевдоним; явное preview_id победа Всегда перечитывайте product after писать.
  • Чтобы удалить main image Обновление — preview_id: "" (пустая строка).
  • `upload.id` идти /done, в то время как `result.id` Прикреплен к нему product. Они отличаются друг от друга. values.
  • file_type это category (image | video | audio | model | file), не MIME type.
  • Один file_id может быть использован в нескольких galleries и products — Не загружайте то же самое file снова.
  • public_path В качестве реакции на хранение используется full объектное хранение URL. На витрине используйте родственника витрины path с обрезкой (/w/{width}/webp{path}), не это URL И не оригинальное.
  • gallery_ids является заменой-set Ссылки: Отправить full Список в The желанный order.

7.1 Галерея является отдельной галереей resource

Галерея не имеет live внутри product. Это отдельный shop субъектом с его own CRUD; products, categories (любой type) Только статьи ссылка это.

Конечная точкаЧто он делает
GET /galleriesтот shop's live galleries с их стороны items, новый first; стриженный — ?page.number=&page.limit= (1.100, без параметров first 50; ответ имеет total и currentPage рядом с result[]
GET /galleries/{id}Одна галерея с ее items
POST /galleriesСоздать галерею вместе с items
PUT /galleries/{id}обновление: name and/or items
DELETE /galleries/{id}мягкий удаление; галерея в использовании cannot быть deleted
GET /galleries/{id}/usageгде используется: products, categories, статьи и variant sets

Форма галереи:

{ "id": "31", "name": "Синие", "source": "images", "canvas_id": "0",
  "items": [
    { "item_key": "8231acd9-…", "kind": "image",
      "file": { "id": "<file_id>", "url": "…" }, "poster": null,
      "html": "", "caption": "вид спереди" },
    { "item_key": "4fba917d-…", "kind": "html", "file": null,
      "html": "<b>Схема сборки</b>", "caption": "" } ] }

Что важно для items:

  • тот role (`kind`) это set По данным сервера, Не от вас, а от того, что вы file type: image | video | audio. Только "html" это set Ручно — у него нет file, только разметка. kind Это не соответствует file type (e.g. "video" на image) → 400 GALLERY_ITEM_KIND_MISMATCH. Нет никакого молчаливого фиксирования: молчаливо заменить role Создает витрину, которая не look как что вы посылали;
  • не-html item требует а file (400 GALLERY_ITEM_FILE_REQUIRED); ан html item Требуется не-empty html (400 GALLERY_ITEM_HTML_REQUIRED) в большинство 64 KB (400 GALLERY_ITEM_HTML_TOO_LONG);
  • poster Это видео poster или аудио cover, то же самое { "id": … };
  • item_key является стабильным item key. Пусто на входе → сервер назначает uuid; твой own Он держится, он держит item "«То же самое» при переупорядочении, так что когда повторная обработка запроса after Тайм-аут, отправьте свой own ключи;
  • items в PUT /galleries/{id} это заменить-set: не-empty Список заменят все items полностью; а empty Список (или недостающий) field) означает "не прикасаться". То ясный Галерея — это empty список плюс "items_replace": true: без Флаг последний item cannot Снимаем с себя все. name редактируется отдельно и независимо: { "item": { "name": "…" } } Не трогать тот items.

Привязка к объектам одинакова для products, categories и статьи:

PUT /products/808    { "item": { "gallery_ids": ["31","32"], "gallery_ids_replace": true } }
PUT /categories/85   { "item": { "gallery_ids": ["31"],      "gallery_ids_replace": true } }
PUT /articles/12     { "item": { "gallery_ids": ["31"],      "gallery_ids_replace": true } }

Над card читать оба fields прибыть: gallery_ids — заказанный set из ссылки, galleries — их содержание. Одна галерея законно сидит на Несколько объектов — Редактирование его содержимого меняет то, как все они look, и это ожидается.

⚠️ galleries может быть дольше чем gallery_ids. Если variant set ссылки Галерея, которая не привязана к product сама по себе, ее содержание все еще в тот card — В конце концов galleries, но не в gallery_ids. Это Преднамеренное: путем отправки GET body обратно в PUT Вы не будете связывать кого-то else's Галерея в сторону product. Отсюда rule: читать set Обязательные от gallery_ids, и look поднимать display Содержание в galleries через id, не position. The preview fallback вычисляется только из BOUND galleries — а product Единственная галерея, которая проходит через set справочные данные preview: null и preview_auto: false. проверенный live.

Галерея в использовании cannot быть deleted: 400 GALLERY_IN_USE: used in N places. Сначала удалите ссылки (на объекты) — gallery_ids: [] + gallery_ids_replace, на sets — gallery_id: "0"), проверять GET /galleries/{id}/usage, и только затем удалять.

пределы: ≤24 items за галерею, name 1.64 Персонажи, ≤15 galleries на объект.

7.2 Что такое молчание ignored — Как проверить, что письмо было effect

Сервер возвращается 200 В таких случаях тоже, но делает что-то другое, чем вы. ожидаемый:

Что ты послалЧто на самом деле произошло
gallery_ids: [] без gallery_ids_replaceЧитать как «Не трогай», «Не связывай все»"
categories: [] без categories_replace"Не трогай". Непустый список заменяет set (§2.3)
price.currencyПустота — Пустота shop'используется валюта; любая другая value чем shop's является 400 CURRENCY_IS_STORE_LEVEL
company_id в запросе bodyуронили; key определяет shop
order Когда создается categoryСекция откладывается; последняя
items на галерее, управляемой документом холста (canvas_id ≠ 0)Сброшенный; сохраненный items оставаться резервной копией до необязательного — включая запрос с canvas_id: 0 и items В общем, непринужденные работы, items не
items: [] В галерее без items_replaceчитать как «не трогать»; для очистки требуется флаг
читать gallery_ids / galleries из списокGET /products У нас таких полей нет вообще., GET /categories возвращает их пустыми — списки не возвращаются galleries, только preview

Проверка after Каждая запись одинакова: перечитывайте объект и compare тот fields Ты послал. А. 200 Ответ доказывает только то, что запрос был разобран..

# wrote
PUT /products/808 { "item": { "gallery_ids": ["31"], "gallery_ids_replace": true } }
# re-read and compared
GET /products/808        # result.gallery_ids == ["31"], result.galleries[0].id == "31"
GET /galleries/31        # items — the ones sent, in the same order
GET /galleries/31/usage  # the list contains product 808

Три fields Особенно легко неправильно понять:

  • preview_auto: true — тот preview Показано как a fallback: тот product иметь none его own, Сервер взял на себя first image item из first Галерея, не пишите это preview обратно в preview_id — Вы получите A дублировать;
  • а set's gallery_id равные "0" — Нет никаких ссылок, или галерея была deleted; Витрина показывает product's first галерея;
  • source: "canvas" В галерее — это items Управляется холстом document (canvas_id), и items Вы посылаете не получить там;
  • galleries более длительный, чем gallery_ids — В конце концов, они galleries ссылаясь на variant sets но не привязан к product.

7.3.Первый product с а photo — сквозной пример

Этот пример был перенесен с быстрого запуска, который сохраняет только безопасные показания. Заполните §1 и согласуйте письмо с owner. Тебе нужен Баш, curl, jq, права создавать products/galleries и загружать files, а JPEG product.jpg, и существующий shop category в CATEGORY_ID переменный. VIZEN_TOKEN иметь уже переданы в процесс надежно в соответствии с Руководство по аутентификации; отслеживание снарядов и header лесозаготовка есть off. И снова бережет, создает new Объекты — это пример для одного согласованный суд, а не идемпотент import.

set -euo pipefail
: "${VIZEN_TOKEN:?Supply the key securely first}"
: "${CATEGORY_ID:?Choose an existing shop category}"
API=https://api.vizen.shop

# 1. Create an unpublished product. The currency comes from the shop settings (§2.1).
PID=$(curl --fail-with-body -sS -X POST "$API/products" \
  -H "Authorization: Bearer $VIZEN_TOKEN" -H 'Content-Type: application/json' \
  -d "$(jq -n --argjson category "$CATEGORY_ID" \
    '{item:{name:"Sample product",price:{price:100},category_id:$category,is_published:false}}')" \
  | jq -er '.result.id | select(. != null)')

# 2. Get an upload link: result.id and upload.id are different identifiers.
UPLOAD=$(curl --fail-with-body -sS -X POST "$API/v1/storages/files" \
  -H "Authorization: Bearer $VIZEN_TOKEN" -H 'Content-Type: application/json' \
  -d '{"section":"content","file_type":"image","original_name":"product.jpg"}')
FILE_ID=$(jq -er '.result.id' <<< "$UPLOAD")
UPLOAD_ID=$(jq -er '.upload.id' <<< "$UPLOAD")
UPLOAD_URL=$(jq -er '.upload.url' <<< "$UPLOAD")

# 3. Upload the raw bytes without the authorization header, then confirm upload.id.
curl --fail-with-body -sS -X PUT "$UPLOAD_URL" \
  -H 'Content-Type: image/jpeg' --data-binary @product.jpg
curl --fail-with-body -sS -X POST "$API/v1/storages/files/$UPLOAD_ID/done" \
  -H "Authorization: Bearer $VIZEN_TOKEN"

# 4. Read the final metadata: conversion may change public_path.
curl --fail-with-body -sS "$API/v1/storages/files/$FILE_ID" \
  -H "Authorization: Bearer $VIZEN_TOKEN"

# 5. Create a gallery and bind it to the product; a separate preview is not needed.
GALLERY_ID=$(curl --fail-with-body -sS -X POST "$API/galleries" \
  -H "Authorization: Bearer $VIZEN_TOKEN" -H 'Content-Type: application/json' \
  -d "$(jq -n --arg file "$FILE_ID" \
    '{item:{name:"Sample gallery",items:[{file:{id:$file}}]}}')" \
  | jq -er '.result.id')
curl --fail-with-body -sS -X PUT "$API/products/$PID" \
  -H "Authorization: Bearer $VIZEN_TOKEN" -H 'Content-Type: application/json' \
  -d "$(jq -n --arg gallery "$GALLERY_ID" \
    '{item:{gallery_ids:[$gallery],gallery_ids_replace:true}}')"

# 6. Verify the result by reading; the product is still unpublished.
curl --fail-with-body -sS "$API/products/$PID" \
  -H "Authorization: Bearer $VIZEN_TOKEN"

Сравните name, price, category, is_published:false и связанная галерея с запросом, который вы отправили; GET /galleries/{id} должен содержать загруженный file. Галерея's first photo служит в качестве product preview. В content sections тот оригинал может быть преобразован в WebP; Используйте финальный public_path after /done, и хранить ссылки на стабильные file_id. Для file в public URL, заменить steps 2-4 с одним from-url § 7; не требуется подтверждение через /done. Для всех других операций галереи используйте § 7.1-7.2 без секунды. set После проверки, передайте мне все, что я могу. result в owner; Этот пример Не публикует ничего.

Чтобы поставить new Картина, где старый, replace тот file's bytes. The file_id Остается прежним, поэтому галерея (gallery_id, каждый item_key, order, caption), preview_id, variant sets, cards и сцены keep Указывая на то же самое file и показать new Ничего на фотографии. product Написано: Нет PUT /products, нет PUT /galleries. Имя, price, categories, SKU и Публикация остается такой, какая она есть. storage:write.

# 1. Read the file and keep what you will compare: public_path (and the old bytes, for a rollback)
GET  /v1/storages/files/<file_id>
# 2. Ask for an upload link for the SAME file
POST /v1/storages/files/<file_id>  { "original_name": "front.png" }
# → result.id = <file_id> (unchanged), upload.id = <file_id>, upload.url
# 3. Bytes without Authorization, then confirm with upload.id
PUT  <upload.url>                          # Content-Type: image/png
POST /v1/storages/files/<file_id>/done  { }
# 4. Verify: same id, new name, new public_path
GET  /v1/storages/files/<file_id>

Правила:

  • The new bytes Показать везде file используется. Один file Можно сидеть несколько galleries и products (§7) Перед массовым бегом постройте карту file_id → uses из GET /galleries (all pages) и products' preview_id. Если разделяемый file должны быть разными pictures для разных products, загружать new file 7 и внесите в галерею с PUT /galleries/{id}: тот full items Список, каждый item_key отправленный обратно без изменений (§7.1)).
  • Заканчивайте каждый file сразу. Правильно. after тот replace тот file уже сейчас указывает в new, все еще empty объект, так что до тех пор, пока PUT и done успех Фотография может отсутствовать на site. А. failed PUT → повторять steps 2-3 для это file.
  • Проверь. bytes before тот replace. done Отказаться от одного empty загружать (400 EMPTY_FILE) или картинка над image limit (400 FILE_TOO_LARGE, 10 MiB через default), и отказ удаляет file: galleries и preview Не надо, не надо, не надо.-empty files внутри limit.
  • PNG и JPEG становиться WebP в done (quality 90, прозрачность сохраняется), когда Это меньше: public_path и изменением протяженности, то есть file_id делать Нет, магазин. links через file_id, never через URL.
  • На сервере нет никаких отмен. Старый bytes не сохраняется как version Ты можешь восстановить., download тот file из public_path before тот replace и keep с его помощью file_id. Откат - это то же самое steps с старый bytes.
  • Конфликты и возобновление. The replace Не имеет «ожидаемого старого» version" field. Прочти file просто before шаг 2 и compare public_path с помощью value в Разное означает, что кто-то изменил список. file, Так что пропустите row. Держи. журнал file_id → done. На повторе, пропустить закончил rows: повторяющийся replace Безвредно, но стоит два more звонки.
  • Отказы: а draft key на live file → 400 FILE_CONTOUR_MISMATCH (использовать live key); другой shop's file или unknown id → 404 NOT_FOUND; а file из одного site-folder освобождение → FILE_IN_RELEASE (publish а new вместо освобождения); 409 FILE_CHANGED_RETRY → тот file В тот же момент он изменился, прочитайте его снова..
  • Скорость. The replace и done делиться shop's file бюджет 180 Позвонить минутку (all Ключи и люди из shop вместе, так ~4 000 files Это займет 45-60 минут.Читать далее (GET) использовать бюджет отдельного чтения. Над 429, ждать Retry-After.

8. Навал import

POST /products/batch
{ "items": [ { … }, { … } ] }

Ответом является a частичный успех: каждый item получает own result или error. Проанализируйте ответ item через item; в целом 200 Не значит, что все Успешно. results[] матч index к оригиналу position: успешный item содержит id, а failed содержит error. Возвращать только failed items Чтобы не дублировать products которые уже были приняты.

Безопасно import order:

  1. Ключевой паспорт (§1), проверка currency и contour, owner подтверждение.
  2. Категория:Настройка order через /categories/move.
  3. атрибуты и options → записывать их ids; Они нужны везде after.
  4. Файлы → Построить local name → file_id карта.
  5. Галереи (POST /galleries) → строить photo set → gallery_id карта. Галерея является created before тот product: тот product ссылаться на него, Не в другую сторону round.
  6. Одна канарейка product, is_published: false → Прочитайте его назад → compare каждый field Вы послали показать owner.
  7. Остальная часть каталога в пакетах.
  8. Ссылки (variants или групп).
  9. Публикация: is_published: true.
  10. Проверка на витрине: category page и product card return 200 и Содержит то, что ожидается.

Императивность. Сервер не сохраняет ваш external product key. Держи. местный manifest external SKU → product id → gallery_id → file_id → slug и Проверить существование before создавать: GET /products/by-slug/{slug}. В противном случае Rerun будет создавать дубликаты.

откат. Не удалять - не публиковать: is_published: false. Это Обратимая, но product'адрес перестает отвечать (404, или постоянный 308) в его раздел — §12.3); catalog_hidden: "true" В одиночку сохраните адрес и Вынимает карту только из сетки.

8.1 Весь каталог — POST /products/import

POST /products/batch Только магазины cards, 100 за раз, и каждый повтор создает new products. Для каталога, который регулярно перезагружается, есть второй конечная точка:

POST /products/import
{ "items": [ {
  "sku": "ART-001",                      # KEY: the row finds its product by it
  "name": "Лампа настольная",
  "slug": "lampa-nastolnaya",            # empty on a new product → generated from the name
  "description": "…",
  "base_price": 4900, "old_price": 6900, # integers, in the SHOP currency
  "category_id": 12,                     # or "category_slug": "svet"
  "status": "new", "is_published": true,
  "tags": ["распродажа"],
  "attributes": [                        # attribute CODES, not ids
    { "code": "height", "value": 42 },
    { "code": "color",  "value": "red" },
    { "code": "tags_multi", "value": ["a", "b"] }
  ],
  "variant_axes": [ { "code": "color", "display": "swatch" } ],
  "variants": [
    { "values": { "color": "red" },  "sku": "ART-001-R", "price": 4900 },
    { "values": { "color": "blue" }, "sku": "ART-001-B" }
  ],
  "images": ["<file_id>", "<file_id>"]   # array order = gallery order
} ] }

Чем отличается от `/products/batch` (конечная точка остается; она не заменяется):

/products/batch/products/import
Rows в запросе1005,000
Что за row держатьcardcard + attributes + топоры и sets + image order
ПовторятьСоздает дубликатыОбновления от `sku`
Адрес продуктаВаш местный manifest SKU → idтот sku само собой

Императивность. sku это required Уникальный в пределах shop. А. row Найдет он свой product с помощью него: none — Один из них created (created); существует — это updated (updated). Местный manifest для повторных загрузок больше не требуется.

Пустота = нетронутая. скалярный field отсутствующий в row остается неизменным на существующий product. Списки (attributes, variants, images) заменить-set: а не-empty Список полностью заменяет set, ан empty Оставляющий его нетронутым, empty Вместе с одним из attributes_replace / variants_replace / images_replace Поле не в контракте, не в договоре. (SEO, preview, сцена, Группировки, запасы) являются NEVER тронутый тем, import — Редактировать их через свои own конечные точки.

Ответом является per row. HTTP 200; каждый row's Результат находится в results[i]:

{ "rows": 3, "created": 1, "updated": 1, "skipped": 1,
  "results": [
    { "row": 1, "product_id": "5001", "action": "created", "errors": [] },
    { "row": 2, "product_id": "4812", "action": "updated", "errors": [] },
    { "row": 3, "product_id": "0",    "action": "skipped",
      "errors": [ { "code": "CATEGORY_NOT_FOUND", "field": "category_id",
                    "message": "категории 99 нет в этом магазине" } ] }
  ] }

row Это ROW number, считать от одного (items[0] → row: 1); тот order из results матчи items. action: "skipped" средство ничего из этого row Поступил в базу данных — нет product, нет attributes, нет sets: партия пишется в одной сделке, и не может быть "половины а" product" в ней.

Коды Row для отказа: SKU_REQUIRED, DUPLICATE_SKU_IN_BATCH (BOTH rows есть Отвергнутый — только file's author Знать, кто из них прав), PRODUCT_NAME_REQUIRED и CATEGORY_REQUIRED (new products только), CATEGORY_NOT_FOUND, CATEGORY_TYPE_MISMATCH, OLD_PRICE_LESS_THAN_PRICE, SLUG_TAKEN, ATTRIBUTE_NOT_FOUND, ATTRIBUTE_VALUE_INVALID, DUPLICATE_ATTRIBUTE_IN_ROW, NO_VARIANT_AXES, VARIANT_AXIS_NOT_SELECT, VARIANT_VALUES_MUST_COVER_AXES, VARIANT_VALUE_UNKNOWN_OPTION, DUPLICATE_VARIANT_COMBO, DUPLICATE_VARIANT_SKU, AXIS_ALREADY_GROUP_AXIS (Атрибут уже является групповой осью product), IMAGE_NOT_FOUND, GALLERY_ITEM_KIND_MISMATCH, GALLERIES_TOO_MANY (тот product Уже есть 15 galleries — тот import Не имеет места для своего own), TOO_MANY_* и ROW_INVALID.

ROW_INVALID — тот row нарушение условий договора: отрицательное product или set price, а field выше длинных кеп, display вне словарного запаса text · icon · swatch. field несёт path в field (base_price, variants[0].price), message тот reason. Ограничения строк проверяются как целые, включая гнездовые attributes, топоры и sets: плохой row отвергается в его own и не сбивает своих соседей.

OLD_PRICE_LESS_THAN_PRICE Сравнение цен AFTER слияние с product's current values. А. price Список, который отправляет только base_price выше хранимых old_price получает row Отказ - отправьте обе цены вместе.

Только просьба недействительна AS А. WHOLE получает HTTP error:

HTTPКодКогда
400валидатор messageempty items или more более 5000 rows
400IMPORT_TOO_MANY_ATTRIBUTES / IMPORT_TOO_MANY_VARIANTS / IMPORT_TOO_MANY_IMAGEStotal пакетный колпачок (1,200,000 / 200 000 / 200 000))
401 / 403AUTH_*, PAT_SCOPE_MISSINGНет, нет, нет. shop, нет catalog:write правильно
403PAT_CONTOUR_MISMATCHdev key: объемный import Пишет только для live shop, и key's contour Уменьшает запрос off before обработчик
413REQUEST_TOO_LARGEbody больше 64 MiB (тот gRPC side принимает тот же)
429IMPORT_RATE_LIMITEDминута ROW бюджет превышен; message Как долго ждать
429PRODUCT_LIMIT_REACHEDплан's product КОБРА: подсчитывает партию's NEW rows; Если места недостаточно, none написанный

тело size. Запрос на кэп составляет 64 MiB, и 5000 rows не always стойка: a row со 100+ attributes весит около 10 KB, Таких 5 тысяч человек rows подходы тот limit. Рекомендация: 100+ attributes за product отправлять нет more чем 1,000 rows по запросу (keep 5,000-row партии для price Список с десятком fields). Превышение его дает явное 413 REQUEST_TOO_LARGE, Не уронить Связь: когда вы видите ее, разделите file Вместо того, чтобы повторять Тот же запрос.

Изображения. images брать file_ids из ALREADY загруженный images (§5; не существует загружать URL Вот, колпачок. 24 кадра за row: Что такое обычный Галерея принимает (§7), поэтому import Не собирайте ничего, что standard editor Позже он отказался от спасения..

Все в одном row's кадры входить в ONE галерея product — его own: created на first import, повторно используется на более поздних; вторая галерея не появляется. own" средство created от import Нигде не используется else — не привязанный к другой product, category или article, не упоминаются в а variant set, включая Если такое связывание существует только в dev draft. Все else иностранный в import: ручная галерея, галерея, расположенная на нескольких cards сразу (тот standard model — "Выбрать галерею из другой product"), холст Галерея. import не изменяет иностранную галерею, создает ее own Если бы это было product Уже есть 15 galleries (Для каждого объекта, row получать GALLERIES_TOO_MANY.

Галерея order Это твой. The import помещает свои own галерея FIRST в product's связующие (The Bindings) first Галерея является лицом card) Только когда это создает. Ан order ты set вручную через PUT /products/{id} (gallery_ids) Не тронуты перезагрузками: если вы поставите manual галерея first, Он остается first Однако много раз в price Если вам нужен импортный first, reorder Обязательные сами.

Что? import изменения в кадрах. Только set из file_ids и их order. Подписи к кадрам (caption), плакаты, html items и рама role есть Ваш: на раме, которая выживает при загрузке (то же самое) file_id) Они хранятся. В кадре больше нет images это deleted Вместе с его caption — Рамка является просто ушла.

Умеренность. Если product Представляется на общий рынок (is_shared) и уже одобрены, меняя покупательский подход content — name, description, цены, category, теги, attributes, Галерея кадров - удаляет ""Одобренный" и возвращает card В очереди на умеренность. rule То же самое как и регулярные product Изменение обнаруживается путем сравнения before/after, не в силу field Скачать: Reuploading the same file Не трогать статусы.

Что? import не делать. Не удаляет products отсутствующий в file (а skipped product Он остается таким, каким был; он не создает categories и attributes — Создавайте их заранее (§ 2, § 3); они не приносят files через URL.


8.2 Скорость импорта

Над 429 RATE_LIMITED Ждать паузы, данной в Retry-After; Не повторять

  • Успешная часть партии. Бюджет запроса распределяется между всеми участниками.

компании, включая другие ключи и staff; числовой limits — /docs/webcoding §10. Записанный загрузчик использует несколько запросов, from-url один; limit Параллельность. 503 честь Retry-After Если он будет возвращен.

9. Ошибки.

The main Форма - это RPC конверт:

{ "error": "rpc error: code = InvalidArgument desc = INVALID_WIDGET" }

Машина code Это first слово after desc = . Общие:

Кодзначение
PAT_METHOD_NOT_ALLOWEDМетод недоступен для ключей этого type вообще
PAT_SCOPE_MISSINGПраво отсутствует; required Один из них назван в message
AUTH_COMPANY_PROBLEMThe shop Не определено - проверьте key
INVALID_WIDGETВиджет снаружи text · select · icon · swatch · photo
INVALID_DATA_TYPEТип снаружи string · number · bool · select · multiselect · date · color · url · group
INVALID_CODE, INVALID_OPTION_VALUEАтрибут code и вариант value Латинские слизни, не читаемые человеком text
FILE_IN_USEThe file cannot быть deleted: он связан
CATEGORY_FILE_NOT_FOUND (404)А. category's preview_id или seo.og_image_id не является а live file твоей shop. Чек прибыл с выпуском «Галереи»: такое письмо раньше проходило бесшумно
URL_NOT_ALLOWED, URL_FETCH_FAILED, FILE_TOO_LARGEЗагрузить URL

Галереи (§7) отвечают own коды:

КодHTTPзначение
GALLERY_NOT_FOUND404Нет такой галереи — либо она принадлежит другому арендатору, либо она была deleted. Один code в обоих случаях: сервер не подтверждает, что другой арендатор's resource существует
GALLERY_IN_USE: used in N places400Галерея используется; удалите ссылки, GET /galleries/{id}/usage показывает, где
GALLERY_NAME_REQUIRED / GALLERY_NAME_TOO_LONG400Имя есть required, максимум 64 символа
GALLERY_TOO_MANY_ITEMS400Более 24 items в галерее
GALLERY_DUPLICATE_ITEM_KEY400Тот же самый item_key появляется дважды
GALLERY_ITEM_KIND_MISMATCH400The role Отправленное не соответствует file type (или files из этого category не показаны в galleries)
GALLERY_ITEM_FILE_REQUIRED400Ан item без kind: "html" не имеет file
GALLERY_ITEM_HTML_REQUIRED400А. kind: "html" item иметь empty разметка
GALLERY_ITEM_HTML_TOO_LONG400Разметка по пунктам превышает 64 KB
GALLERY_FILE_NOT_FOUND404file_id или poster.id не является а live file твоей shop
GALLERIES_TOO_MANY400Более 15 galleries на объект
GALLERY_DUPLICATE_LINK400Одна и та же галерея дважды в списке gallery_ids
GALLERY_MANAGED_BY_CANVAS400Ты cannot Позволяет холст mode и посылать items В одном запросе: свяжитесь first, затем items
GALLERY_DUPLICATE_CANVAS400Это полотно document Управляет еще одной галереей shop
CANVAS_DOCUMENT_NOT_FOUND404canvas_id не является а live document твоей shop
CANVAS_TYPE_MISMATCH400The document существует, но является неправильным type

⚠️ Более 15 values в gallery_ids Отвергаются одним-item конечная точка before машина code — с валидатором message (value must contain no more than 15 item(s)). голый GALLERIES_TOO_MANY code исходит из POST /products/batch, где ошибки возвращаются в item.

Есть ли у тебя второй Форма — для одного unknown path. Оно приходит вместе с 404 и содержит намек:

{ "error": "not_found", "path": "/v1/products/808",
  "hint": "catalog is unprefixed (/products); storage/account/org under /v1",
  "docs": "/openapi.json" }

Клиент должен уметь разбирать и то, и другое. Отличительный признак: наличие тот path field.

The load Ограничитель отвечает 429 и указывает время ожидания — уважайте его и повторное рассмотрение просьбы after пауза.


10. Условия остановки.

Остановитесь и спросите owner если:

  • тот shop currency не соответствует price список currency;
  • тот key пишет в live shop И не было подтверждения для объемного письма;
  • after Творение (Создание) product не найдено в его category filter;
  • а value Отличается от отправленного, и это не описано здесь;
  • а public page Ответить на что-то другое, чем 200;
  • а 429 Прибывает и возвращается after Пауза не помогает.

И не подлежащий обсуждению rule: тот key never заканчивается в любом file, вести, HTML, отчет или message. Он находится в отдельном магазине с 600 разрешения.


11. Ссылка: fields С сюрпризами

ПолеТипЧто важно
price.priceцелое числоВ этом shop Валюта. Никакой дробной части
price.currencyструнаПустота на письме = shop Валюта – другая валюта 400 CURRENCY_IS_STORE_LEVEL. Ответ несет в себе shop валюта
category_idцелое числопервичный category
categories[]множество целых чиселДополнительные. Обновление непустого списка заменять тот set; Потребности в очищении [] + categories_replace: true
preview / preview_idобъект / строкасоздавать — preview: {id}; обновление — preview_id: "…" (preview: {id} Там есть псевдоним; preview_id: "" удаляет image)
catalog_hiddenструна"" / "true" / "false" — не булевой
is_publishedбулевыйОбычный флаг
order (category)целое числоИгнорируемые на создание — Раздел будет последним. order через /categories/move
widget (атрибут)струнаtext · select · icon · swatch · photo
display (ось)струна"" · text · icon · swatch — более узкое, чем widget
data_type (атрибут)струнаОсь переключателя всегда select
values (set)объектattribute_id (как струна опция value
gallery_idsмассив струныГалерея Ссылки (int64 непустая замена; пустая без gallery_ids_replace → "Не трогать"
galleriesмассив объектовТолько на карточке прочти. Пусто в списках. Может быть длиннее, чем на карточке. gallery_ids: В конце — galleries ссылочные наборы
items_replace (галерея)булевыйНужно очистить items: пустой items Без флага = "не трогать""
gallery_id (set)струнаЛюбые живые галереи shop, не только "своего" own". "0" = Никаких ссылок или галереи не было удалено.
kind (галерея item)струнаустановленный сервер от file type. Ручно — только "html". Несоответствие = отказ
item_key (галерея item)струнастабильный key. Пустой - сервер присваивает один; ваш own в повторном порядке
source, canvas_id (галерея)струна"canvas" = items Управляется документом Canvas; items Отправлено игнорируется
preview_autoбулевыйtrue = preview Заполняет запасной вариант из галереи. preview_id
public_path (file)струнаПолное хранилище URL. На витрине — родственник path с обрезкой
page.numberцелое числоНачало 1.Необходимо, если page.limit Проходит
group_info, variants_info, attribute_sections, categoriesобъектыБратья и сестры result в product Ответ, а не его поле

12.Изменение того, что уже существует

Измеряется на стенде 2026-10-28.

12.1. PUT /products/{id} Меняйте только то, что вы отправляете

отсутствующий field Сохраняет свои value — is_published, catalog_hidden, name, category_id Отправляй поля, которые ты меняешь, и ничего больше. Эти поля: вот где небольшое исправление теряет данные.

Ты посылаешьЧто происходит
price: {price: 900} без old_priceПрорыв через price становится 0: price Отправь обоих или уйди. price выходить. Никогда не возвращайте `price` Ты читала в то время как price.promotion_name установлен или price.price отличающиеся от price.catalog_price: Чтение показывает, price after поощрения и price Правила, и написание его делает скидку постоянной. price это price.catalog_price
seo с одним fieldпустой text поля (slug, Заголовки, описание, ключевые слова) сохранены, но og_image_id, noindex и nofollow Переписывается — а seo Без них они будут устранены. image и оба флага. Отправьте все три с каждым seo
categories: [X], tags: [X]заменить Весь список с [X] (§2.3); [] Ничего не меняет
description: "", sku: "", preview_id: ""ясно, что field. name: "" это 400
stock_quantityНедооцененный; запас PUT /products/{id}/stock, Закрытые ключи (/docs/stock)

Атрибутов в этом нет body (неизвестный field это 400 INVALID_REQUEST): PUT /products/{id}/attributes Заменять собой product'весь set из values.

12.2 Один field на многих products

  1. Снимок. Читать каждый product Вы будете трогать и держать JSON — Это Ваш откат. Продолжайте. price.catalog_price как основание price, не price.price (§12.1). GET /products?attribute_codes=… Ответы как покупатель: Он оставляет неопубликованным и скрытым products и attributes скрытый от покупатели (/docs/characteristics §5) - читать их на product.
  2. Канарская. Напиши одну product, Перечитывайте, открывайте page.
  3. Писать. Продукты с одной sku: POST /products/import с рядами {"sku": …, <the field>} — Отсутствующие скалярии сохраняют свои value, Один запрос на 5 000 строк. Без sku: PUT /products/{id} за product (§12.1). Атрибуты являются заменой в обоих: Чтобы изменить один, отправьте product's полный набор, построенный из снимка, или остальные теряются.
  4. проверять Прочитав все и сравнив с планом.
  5. откат = То же самое пишут со снимком values (Цены как на шаг 1).

12.3 Возьми product off Продажа и возврат

Для покупателяis_published: falsecatalog_hidden: "true"
карточка (GET /products/{id})404открывать
адрес магазинаНе найдено: постоянный 308 в ближайший к нему раздел адреса, когда shop's miss_fallback находится (/docs/redirects), иначе 404открывать
Сети разделов, поиск, подсчет разделовуходитьуходить
коммутатор группуходитьоставаться
карта сайтауходитьоставаться
Добавление в корзинуотказыватьсяразрешенный

А. product - Уже в тележке, она там и может быть заказана. с is_published: true или catalog_hidden: "". А. 308 является постоянным: а браузер, который последовал за ним, может продолжать переходить в раздел after тот product это назад. catalog_hidden Это пауза в сетках и поиск только — product это Продается по адресу; только is_published: false Прекратить продажу.

12.4 Копии и удаление

  • `POST /products/{id}/copy`, body {} → {"new_id": …}. То же самое shop, и а выталкивать key Только (Dev) key это 403 PAT_CONTOUR_MISMATCH). Копия является Copy of <name>, Неопубликованный, с пустым sku и новый slug. Он держится Описание, цены, categories, теги, preview, SEO, Сцена (как новая) копия), attributes, содержание blocks, различные оси и наборы (их sku очищенный), combo содержание и Ссылки на один и тот же galleries — редактирование такого Галерея меняет оба products. Не копировать: catalog_hidden, Группировка, обзоры, уровни запасов, отношения, мульти-product Содержание (копия является standard).
  • `DELETE /products/{id}`, body {} (Мягкий: ответ несет deleted_after (+30 дней, но есть Нет восстановления и удаленный product Его адрес 308-е до последнего. category (miss_fallback, /docs/redirects). Заказы Они сохраняют свои линии, оставляя тележки, группы и комбо (и комбо). combo price).
  • `DELETE /categories/{id}`, body {}. Отказ, если у него есть подкатегории (CATEGORY_HAS_CHILDREN), products — первичный или дополнительный, неопубликованный (CATEGORY_HAS_PRODUCTS) — или статьи (CATEGORY_HAS_ARTICLES); Домой page это HOME_CATEGORY_UNDELETABLE. Перемести products Первый (§12.1)).

12.5 Наборы и мультипликаторы-products

  • А. set (combo): PUT /products/{id}/combo-items {"items":[{"component_product_id":6,"quantity":1,"price_mode":"inherit"}]} — заменить-set максимум 20 items. Не пустой список делает product а combo, Пустой возвращает его standard. price_mode: inherit · fixed · percent · free с price_value. Не гнездиться (COMBO_IS_COMPONENT, COMPONENT_IS_COMBO), Никаких дубликатов, никаких иностранных или удаленные компоненты (COMPONENT_NOT_FOUND). Читать: GET /products/{id}/combo.
  • Многократный-product (контейнер): PUT /products/{id}/kind {"kind":"multi"}, затем PUT /products/{id}/children {"items":[{"product_id":5,"sort_order":1}]} — заменить-set максимум 500 standard products, Каждый в одном контейнере Только.Возврат к standard только когда содержимое пусто (PRODUCT_HAS_CHILDREN). combo никогда set через /kind.
  • Не посылать `combo-items` Чтобы многократно-product: не отказано и Он превращает контейнер в set.

12.6 Группы

PUT /product-groups/{id} изменениях, name только; …/members и …/axes есть замещающие наборы (§6)). DELETE /product-groups/{id} с {} удаляет members и оси сразу: products есть free для другой группы и их карточки делают Не меняться иначе.

12.7 Переместить каталог в другой shop

Нет никакого export Без креста-shop Копия. key Работает в одном shop, Так что тебе нужно а key за shop.

  1. Прочти source: Список, затем каждая карта — список не несет описание, galleries или attributes Скрытые от покупателей.
  2. Структура в целевой: categories, затем attributes с их стороны options и этикетки, то каждый атрибут покупатель доступ (PUT /attributes/{id}/access — новый attributes скрытый, /docs/characteristics).
  3. Файлы: а file одного shop не видны другим. воссоздать каждый с его публичного обращения к POST /v1/storages/files/from-url, или загрузить тот bytes (§7).
  4. Продукты: POST /products/import ряды sku, с новым category и file Сначала Канарейка. import не носить с собой catalog_hidden — набор с ним PUT /products/{id} после.
  5. Связи между products — Группировки (§6), множества и многообразия-products (§12.5) — re-created after все products Есть, с новыми ИД.
  6. проверять цель field через field против source.

неподвижный по key: Уровень запасов (только кабинки), /docs/stock) и взгляд Промоушен. (promotions:*) и price правила (pricing:*) есть created Опять в цель shop, с новым product ids.


13 Где дальше

ДокументацияО нас
/docs/webcodingСтраница layout, HTML blocks, images и обрезка, limits
/openapi.jsonПолный API schema
/v1/account/tokenКлючевой паспорт: contour, shop, currency, права
/llms.txtКороткий API Карта для одного AI агент

Исходник: https://api.vizen.shop/docs/catalog-import?full=1

Работает в демо-режиме