
## CRM product fields

`GET /v1/products/fields`

Returns the product field schema: 22 standard fields and the custom catalog properties of the form `PROPERTY_<N>` configured in the Bitrix24 account. Every field carries a label, a type, and a read-only flag. Fields that need further explanation also carry a description, and fields with a fixed value set carry an `enum` dictionary.

## Standard fields

Each field is described by an object `{ type, readonly }`. The `writeOnly: true` marker identifies a write-only name, while `notReturned: true` identifies a name absent from responses. The `list`, `get`, `search`, `create`, and `update` responses use canonical camelCase names. `currencyId` is a write alias: response data carries the currency under `currency`. If both names are present, `currency` wins. An explicit `select=currencyId` is rejected with `400 SELECT_FIELD_NOT_RETURNED`. Use `select=currency` for reads. The native Bitrix24 name `select=CURRENCY_ID` remains valid and also returns the value under the canonical `currency` key.

| Field | Bitrix24 | Type | RO | Description |
|------|----------|-----|:--:|---------|
| `id` | `ID` | number | yes | Product identifier |
| `name` | `NAME` | string | | Product name |
| `active` | `ACTIVE` | boolean | | Whether the product is active |
| `price` | `PRICE` | number | | Product price |
| `currencyId` | `CURRENCY_ID` | string | | Write alias of `currency`. Marked `writeOnly: true` and `notReturned: true` |
| `currency` | `CURRENCY_ID` | string | | Canonical name of the price currency. If both names are present, `currency` wins. List: [`GET /v1/currencies`](/docs/entities/currencies/list) |
| `sectionId` | `SECTION_ID` | number | | Catalog section. List: [`GET /v1/product-sections`](/docs/entities/product-sections/list) |
| `catalogId` | `CATALOG_ID` | number | | Catalog identifier. List: [`GET /v1/catalogs`](/docs/entities/catalogs/list) |
| `measure` | `MEASURE` | number | | Measurement unit identifier |
| `description` | `DESCRIPTION` | string | | Product description |
| `descriptionType` | `DESCRIPTION_TYPE` | string | | Description format — `text` or `html` |
| `sort` | `SORT` | number | | Sort order. A lower value places the product higher |
| `code` | `CODE` | string | | Symbolic product code |
| `xmlId` | `XML_ID` | string | | External identifier for synchronization |
| `vatId` | `VAT_ID` | number | | VAT rate identifier |
| `vatIncluded` | `VAT_INCLUDED` | boolean | | Whether VAT is included in the price |
| `previewPicture` | `PREVIEW_PICTURE` | object | yes | Image for the list |
| `detailPicture` | `DETAIL_PICTURE` | object | yes | Image for the card |
| `createdBy` | `CREATED_BY` | number | yes | Creator identifier. List: [`GET /v1/users`](/docs/entities/users/list) |
| `modifyBy` | `MODIFIED_BY` | number | yes | Identifier of the last editor. List: [`GET /v1/users`](/docs/entities/users/list) |
| `createdAt` | `DATE_CREATE` | datetime | yes | Creation date |
| `updatedAt` | `TIMESTAMP_X` | datetime | yes | Last modification date |

## Custom properties

Catalog properties come back as additional keys of the form `PROPERTY_<N>` with the type `product_property`. Each is described by an object `{ type, readonly, label }`, where `label` is the property name in the Bitrix24 account language. The property set depends on the catalog settings: one Bitrix24 account may return "SKU", "Manufacturer", and "Color", while another returns a list of its own. The values of these properties are returned in the [`GET /v1/products/:id`](/docs/entities/products/get) response, and in [list](/docs/entities/products/list) and [search](/docs/entities/products/search) responses when you include the property name in `select`.

## Examples

### curl — personal key

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

### curl — OAuth application

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

### JavaScript — personal key

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

const { success, data } = await res.json()
console.log('Total fields:', Object.keys(data.fields).length)
```

### JavaScript — OAuth application

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

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

## Response fields

The `label` and `description` field captions are returned in English, while the ones the platform takes straight from the Bitrix24 account come in the account language. Request headers do not switch the language. Enum values carry an English `label`.

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data.fields` | object | Field map. The key is the field name, and the value is `{ type, readonly, label }`. Write-only fields also carry `writeOnly`, non-returned fields carry `notReturned`, and some fields carry `description` or `enum` |

## Response example

A subset of fields is shown. The actual number of `PROPERTY_<N>` properties depends on the catalog settings in the Bitrix24 account.

```json
{
  "success": true,
  "data": {
    "fields": {
      "id": { "type": "number", "readonly": true, "label": "Product identifier" },
      "name": { "type": "string", "readonly": false, "label": "Product name" },
      "active": { "type": "boolean", "readonly": false, "label": "Active", "description": "Whether the product is active." },
      "price": { "type": "number", "readonly": false, "label": "Product price" },
      "currencyId": {
        "type": "string",
        "readonly": false,
        "writeOnly": true,
        "notReturned": true,
        "label": "Price currency (alias)",
        "description": "Write alias of currency. Responses return the value under currency. If both names are present, currency wins. List: GET /v1/currencies."
      },
      "currency": {
        "type": "string",
        "readonly": false,
        "label": "Price currency",
        "description": "Currency of the price. Write alias: currencyId. If both names are present, currency wins. Available values: GET /v1/currencies."
      },
      "descriptionType": {
        "type": "string",
        "readonly": false,
        "label": "Description format",
        "description": "Format of the description field: plain text or HTML markup.",
        "enum": [
          { "value": "text", "label": "Plain text" },
          { "value": "html", "label": "HTML" }
        ]
      },
      "vatIncluded": { "type": "boolean", "readonly": false, "label": "VAT included", "description": "Whether the VAT is already included in the price." },
      "createdAt": { "type": "datetime", "readonly": true, "label": "Creation date" },

      "PROPERTY_301": { "type": "product_property", "readonly": false, "label": "SKU" },
      "PROPERTY_303": { "type": "product_property", "readonly": false, "label": "Manufacturer" },
      "PROPERTY_307": { "type": "product_property", "readonly": false, "label": "Color" }
    }
  }
}
```

## Error response example

403 — no scope:

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

## Errors

| HTTP | Code | Description |
|------|-----|---------|
| 403 | `SCOPE_DENIED` | The API key lacks the `crm` scope |
| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |
| 429 | `RATE_LIMITED` | Rate limit exceeded: 300 requests per minute per portal. All API keys of the portal share one limit. The current limit is returned in the `x-ratelimit-limit` header. Retry after the delay in the `Retry-After` header |

For the full list of common API errors, see [Errors](/docs/errors).

## Known specifics

**The same product in the catalog has more fields.** The product with the same `id` is available via [`GET /v1/catalog-products`](/docs/entities/catalog-products), where it has a wider field set — stock, warehouse, and variations.

**A `PROPERTY_<N>` descriptor carries the property name only — it holds no list options.** The descriptor object is exactly `{ type: "product_property", readonly, label }`: no `items`, no `values`, no list of allowed values whatsoever. So this endpoint tells you that property `301` is called "SKU", but nothing here resolves the selected option's identifier into its text.

Where to go for the values:

| Task | Call |
|---|---|
| Readable text of a property on a **single** product | [`GET /v1/catalog-products/:id`](/docs/entities/catalog-products/get) — the text arrives ready-made in `propertyNNN.valueEnum` |
| **All** options of a list property (a dropdown, a filter, an export) | [`GET /v1/catalog-product-property-enums?filter[propertyId]=NNN&limit=1000`](/docs/entities/catalog-product-property-enums/list) |
| The property type (`propertyType`, `listType`, `multiple`) | [`GET /v1/catalog-product-properties/:id`](/docs/entities/catalog-product-properties/get) |

Here `NNN` is the number from the `PROPERTY_<N>` key: it is the same value, the catalog property `id`. All three catalog calls require the `catalog` scope — a key carrying only `crm` gets `403 SCOPE_DENIED`.

## See also

- [Get CRM product](/docs/entities/products/get)
- [List CRM products](/docs/entities/products/list)
- [Aggregate CRM products](/docs/entities/products/aggregate)
- [Entity API](/docs/entity-api)
- [Catalog products](/docs/entities/catalog-products)
- [Catalog product properties](/docs/entities/catalog-product-properties)
- [List-property values](/docs/entities/catalog-product-property-enums)
