
## Поля товара

`GET /v1/products/fields`

Возвращает схему полей товара: 22 стандартных поля и пользовательские свойства каталога вида `PROPERTY_<N>`, настроенные на портале. Для каждого поля указаны подпись, тип и признак «только для чтения». Там, где есть что добавить, приходит описание, а у полей с фиксированным набором значений — словарь `enum`.

## Примеры

### curl — личный ключ

```bash
curl "https://vibecode.bitrix24.tech/v1/products/fields" \
  -H "X-Api-Key: YOUR_API_KEY"
```

### curl — OAuth-приложение

```bash
curl "https://vibecode.bitrix24.tech/v1/products/fields" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN"
```

### JavaScript — личный ключ

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/products/fields', {
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})

const { success, data } = await res.json()
console.log('Всего полей:', Object.keys(data.fields).length)
```

### JavaScript — OAuth-приложение

```javascript
const res = await fetch('https://vibecode.bitrix24.tech/v1/products/fields', {
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
})

const { success, data } = await res.json()
```

## Стандартные поля

Каждое поле описано объектом `{ type, readonly }`. Признак `writeOnly: true` отмечает имя только для записи, а `notReturned: true` — имя, которое не возвращается в ответах. В ответах `list`, `get`, `search`, `create`, `update` поля возвращаются в camelCase под каноническими именами. `currencyId` — дополнительное имя для записи: значение валюты в ответе приходит как `currency`. Если переданы оба имени, используется `currency`. Явный `select=currencyId` отклоняется с `400 SELECT_FIELD_NOT_RETURNED`. Для чтения используйте `select=currency`. Нативное имя Битрикс24 `select=CURRENCY_ID` остаётся допустимым и также возвращает значение под каноническим ключом `currency`.

| Поле | Битрикс24 | Тип | RO | Описание |
|------|----------|-----|:--:|---------|
| `id` | `ID` | number | да | Идентификатор товара |
| `name` | `NAME` | string | | Название товара |
| `active` | `ACTIVE` | boolean | | Активен ли товар |
| `price` | `PRICE` | number | | Цена товара |
| `currencyId` | `CURRENCY_ID` | string | | Дополнительное имя `currency` для записи. Имеет признаки `writeOnly: true` и `notReturned: true` |
| `currency` | `CURRENCY_ID` | string | | Каноническое имя валюты цены. Если переданы оба имени, используется `currency`. Список: `GET /v1/currencies` |
| `sectionId` | `SECTION_ID` | number | | Раздел каталога. Список: `GET /v1/product-sections` |
| `catalogId` | `CATALOG_ID` | number | | Идентификатор каталога. Список: `GET /v1/catalogs` |
| `measure` | `MEASURE` | number | | Идентификатор единицы измерения |
| `description` | `DESCRIPTION` | string | | Описание товара |
| `descriptionType` | `DESCRIPTION_TYPE` | string | | Формат описания — `text` или `html` |
| `sort` | `SORT` | number | | Порядок сортировки. Меньшее значение — выше |
| `code` | `CODE` | string | | Символьный код товара |
| `xmlId` | `XML_ID` | string | | Внешний идентификатор для синхронизации |
| `vatId` | `VAT_ID` | number | | Идентификатор ставки НДС |
| `vatIncluded` | `VAT_INCLUDED` | boolean | | Включён ли НДС в цену |
| `previewPicture` | `PREVIEW_PICTURE` | object | да | Изображение для списка |
| `detailPicture` | `DETAIL_PICTURE` | object | да | Изображение для карточки |
| `createdBy` | `CREATED_BY` | number | да | Идентификатор создателя. Список: `GET /v1/users` |
| `modifyBy` | `MODIFIED_BY` | number | да | Идентификатор последнего редактора. Список: `GET /v1/users` |
| `createdAt` | `DATE_CREATE` | datetime | да | Дата создания |
| `updatedAt` | `TIMESTAMP_X` | datetime | да | Дата последнего изменения |

## Пользовательские свойства

Свойства каталога приходят дополнительными ключами вида `PROPERTY_<N>` с типом `product_property`. Каждое описано объектом `{ type, readonly, label }`, где `label` — название свойства на языке портала. Набор свойств зависит от настроек каталога: один портал может вернуть «Артикул», «Производитель», «Цвет», другой — собственный список. Значения этих свойств возвращаются в ответе [`GET /v1/products/:id`](/docs/entities/products/get), а в [списке](/docs/entities/products/list) и [поиске](/docs/entities/products/search) — когда нужное свойство названо в `select` своим именем.

## Поля ответа

Подписи полей `label` и `description` приходят на русском языке, а те, что платформа берёт напрямую с портала, — на языке портала. Заголовками запроса язык не переключается. У значений `enum` подпись `label` английская, русская — в `labelRu`.

| Поле | Тип | Описание |
|------|-----|---------|
| `success` | boolean | Всегда `true` при успехе |
| `data.fields` | object | Карта полей. Ключ — имя поля, значение — `{ type, readonly, label }`. У полей только для записи дополнительно приходит `writeOnly`, у невозвращаемых — `notReturned`. Часть полей также содержит `description` или `enum` |

## Пример ответа

Показана часть полей. Реальное количество свойств `PROPERTY_<N>` зависит от настроек каталога на портале.

```json
{
  "success": true,
  "data": {
    "fields": {
      "id": { "type": "number", "readonly": true, "label": "Идентификатор товара" },
      "name": { "type": "string", "readonly": false, "label": "Название товара" },
      "active": { "type": "boolean", "readonly": false, "label": "Активность", "description": "Активен ли товар." },
      "price": { "type": "number", "readonly": false, "label": "Цена товара" },
      "currencyId": {
        "type": "string",
        "readonly": false,
        "writeOnly": true,
        "notReturned": true,
        "label": "Валюта цены — дополнительное имя",
        "description": "Дополнительное имя currency для записи. В ответах значение приходит в поле currency. Если переданы оба имени, используется currency. Список: GET /v1/currencies."
      },
      "currency": {
        "type": "string",
        "readonly": false,
        "label": "Валюта цены",
        "description": "Валюта цены. Дополнительное имя для записи: currencyId. Если переданы оба имени, используется currency. Список доступных значений: GET /v1/currencies."
      },
      "descriptionType": {
        "type": "string",
        "readonly": false,
        "label": "Формат описания",
        "description": "Формат поля description: обычный текст или HTML-разметка.",
        "enum": [
          { "value": "text", "label": "Plain text", "labelRu": "Текст" },
          { "value": "html", "label": "HTML", "labelRu": "HTML" }
        ]
      },
      "vatIncluded": { "type": "boolean", "readonly": false, "label": "НДС включён в цену", "description": "Включён ли НДС в цену товара." },
      "createdAt": { "type": "datetime", "readonly": true, "label": "Дата создания" },

      "PROPERTY_301": { "type": "product_property", "readonly": false, "label": "Артикул" },
      "PROPERTY_303": { "type": "product_property", "readonly": false, "label": "Производитель" },
      "PROPERTY_307": { "type": "product_property", "readonly": false, "label": "Цвет" }
    }
  }
}
```

## Пример ответа при ошибке

403 — нет скоупа:

```json
{
  "success": false,
  "error": {
    "code": "SCOPE_DENIED",
    "message": "This endpoint requires 'crm' scope"
  }
}
```

## Ошибки

| HTTP | Код | Описание |
|------|-----|---------|
| 403 | `SCOPE_DENIED` | API-ключу не хватает скоупа `crm` |
| 401 | `MISSING_API_KEY` | Не передан заголовок `X-Api-Key` |

Полный список общих ошибок API — [Ошибки](/docs/errors).

## Известные особенности

**Тот же товар в каталоге имеет больше полей.** Товар с тем же `id` доступен через [`GET /v1/catalog-products`](/docs/entities/catalog-products), где у него шире набор полей — остатки, склад и вариации.

**Дескриптор `PROPERTY_<N>` несёт только название свойства — вариантов списка в нём нет.** Объект описания устроен ровно как `{ type: "product_property", readonly, label }`: ни `items`, ни `values`, ни какого-либо перечня допустимых значений. Поэтому по этому эндпоинту можно узнать, что свойство `301` называется «Артикул», но развернуть ID выбранного варианта в его текст — нечем.

Куда идти за значениями:

| Задача | Вызов |
|---|---|
| Читаемый текст свойства у **одного** товара | [`GET /v1/catalog-products/:id`](/docs/entities/catalog-products/get) — текст приходит готовым в `propertyNNN.valueEnum` |
| **Все** варианты списочного свойства (выпадашка, фильтр, экспорт) | [`GET /v1/catalog-product-property-enums?filter[propertyId]=NNN&limit=1000`](/docs/entities/catalog-product-property-enums/list) |
| Тип свойства (`propertyType`, `listType`, `multiple`) | [`GET /v1/catalog-product-properties/:id`](/docs/entities/catalog-product-properties/get) |

Здесь `NNN` — число из ключа `PROPERTY_<N>`: это одно и то же значение, `id` свойства каталога. Всем трём каталожным вызовам нужен скоуп `catalog` — ключ со скоупом только `crm` получит `403 SCOPE_DENIED`.

## Смотрите также

- [Получить товар](/docs/entities/products/get)
- [Список товаров](/docs/entities/products/list)
- [Агрегация товаров](/docs/entities/products/aggregate)
- [Entity API](/docs/entity-api)
- [Товары каталога](/docs/entities/catalog-products)
- [Свойства товаров каталога](/docs/entities/catalog-product-properties)
- [Значения списочных свойств](/docs/entities/catalog-product-property-enums)
