For AI agents: markdown of this page — /docs-content-en/entities/invoices/products-fields.md documentation index — /llms.txt
Invoice product fields
GET /v1/invoices/:id/products/fields
Returns descriptions of the fields on invoice product rows: names, types, and whether each field is readable and writable.
The discount amount is called
discount— matching the data and the write contract. The previous namediscountSumremains a deprecated alias: it is still returned by this endpoint and still accepted on write, so code written against the old field list keeps working. Product rows themselves carry onlydiscount— migrate to it.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
Parameterid (path) |
Typenumber | Requiredyes | DescriptionInvoice ID |
Examples
curl — personal key
curl "https://vibecode.bitrix24.com/v1/invoices/741/products/fields" \
-H "X-Api-Key: YOUR_API_KEY"
curl — OAuth application
curl "https://vibecode.bitrix24.com/v1/invoices/741/products/fields" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN"
JavaScript — personal key
const res = await fetch('https://vibecode.bitrix24.com/v1/invoices/741/products/fields', {
headers: {
'X-Api-Key': 'YOUR_API_KEY',
},
})
const { success, data } = await res.json()
console.log('Fields:', Object.keys(data).length)
JavaScript — OAuth application
const res = await fetch('https://vibecode.bitrix24.com/v1/invoices/741/products/fields', {
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
})
const { success, data } = await res.json()
Response fields
| Field | Type | Description |
|---|---|---|
Fieldsuccess |
Typeboolean | DescriptionAlways true on success |
Fielddata |
Typeobject | DescriptionProduct row field dictionary. The key is the field name, and the value is its definition |
Fielddata.<field>.type |
Typestring | DescriptionValue type: integer, double, string, char |
Fielddata.<field>.isReadOnly |
Typeboolean | DescriptionRead-only: the field cannot be written |
Fielddata.<field>.isRequired |
Typeboolean | DescriptionRequired flag. false on every product-row field: a write accepts a row that carries at least one writable field |
Fielddata.<field>.isImmutable |
Typeboolean | Descriptiontrue on ownerId and ownerType: the row owner is set by the request path. Absent on priceAccount and xmlId |
Fielddata.<field>.isMultiple |
Typeboolean | Descriptionfalse on product row fields. Absent on priceAccount and xmlId |
Fielddata.<field>.isDynamic |
Typeboolean | Descriptionfalse on product row fields. Absent on priceAccount and xmlId |
Fielddata.<field>.title |
Typestring | DescriptionField label. Absent on priceAccount and xmlId |
Fielddata.<field>.description |
Typestring | DescriptionField note. Present on id and discountSum |
Fieldmeta.warnings |
Typearray | DescriptionPresent only when Bitrix24 described some of the fields but not all. An item with the code fields_partial lists the fields whose definitions were derived without labels |
Product row fields described by data:
| Field | Type | RO | Required | Description |
|---|---|---|---|---|
Fieldid |
Typeinteger | ROyes | Required | DescriptionRow ID |
FieldproductId |
Typeinteger | RO | Required | DescriptionProduct ID. Catalog: GET /v1/products. A row without productId is created with productId set to 0 and the name from productName — see Add a product |
FieldproductName |
Typestring | RO | Required | DescriptionProduct name |
Fieldprice |
Typedouble | RO | Required | DescriptionPrice |
Fieldquantity |
Typedouble | RO | Required | DescriptionQuantity |
Fielddiscount |
Typedouble | RO | Required | DescriptionDiscount amount. Applied when discountTypeId is 1 |
FielddiscountSum |
Typedouble | RO | Required | DescriptionDeprecated alias of discount — accepted on write, absent from product rows |
FielddiscountRate |
Typedouble | RO | Required | DescriptionDiscount percentage. Applied when discountTypeId is 2 |
FielddiscountTypeId |
Typeinteger | RO | Required | DescriptionHow the discount is set: 1 — as an amount in discount, 2 — as a percentage in discountRate. Defaults to 2 |
FieldtaxRate |
Typedouble | RO | Required | DescriptionTax (%) |
FieldtaxIncluded |
Typechar | RO | Required | DescriptionTax included in price (Y/N) |
FieldpriceExclusive |
Typedouble | ROyes | Required | DescriptionPrice excluding tax, with discount |
FieldpriceNetto |
Typedouble | ROyes | Required | DescriptionNet price |
FieldpriceBrutto |
Typedouble | ROyes | Required | DescriptionGross price |
FieldmeasureCode |
Typeinteger | RO | Required | DescriptionUnit of measure code |
FieldmeasureName |
Typestring | ROyes | Required | DescriptionUnit of measure |
Fieldcustomized |
Typechar | ROyes | Required | DescriptionModified (Y/N) |
Fieldsort |
Typeinteger | RO | Required | DescriptionSort order |
Fieldtype |
Typeinteger | ROyes | Required | DescriptionProduct type — the same value as the product's type in GET /v1/catalog-products/:id: 1 — simple product, 3 — parent product with variations, 7 — service |
FieldstoreId |
Typeinteger | ROyes | Required | DescriptionWarehouse ID |
FieldtaxName |
Typestring | ROyes | Required | DescriptionTax name. Present only in the dictionary, never in product rows. In a write request body it is rejected with 400 INVALID_PARAMS |
FieldownerId |
Typeinteger | ROyes | Required | DescriptionOwner (invoice) ID |
FieldownerType |
Typestring | ROyes | Required | DescriptionOwner type |
FieldpriceAccount |
Typedouble | ROyes | Required | DescriptionPrice in the reporting currency |
FieldxmlId |
Typestring | ROyes | Required | DescriptionExternal code of the row |
Response example
{
"success": true,
"data": {
"id": { "type": "integer", "isRequired": false, "isReadOnly": true, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "ID", "description": "Row identity. Read-only as an attribute, and NOT a handle for editing: PUT /products does not preserve it — a row whose fields change comes back with a new id even if the previous one was sent. To edit a row and keep its id use PATCH /products/{rowId}." },
"ownerId": { "type": "integer", "isRequired": false, "isReadOnly": true, "isImmutable": true, "isMultiple": false, "isDynamic": false, "title": "Owner ID" },
"ownerType": { "type": "string", "isRequired": false, "isReadOnly": true, "isImmutable": true, "isMultiple": false, "isDynamic": false, "title": "Owner type" },
"productId": { "type": "integer", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Product" },
"productName": { "type": "string", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Product name" },
"price": { "type": "double", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Price" },
"priceExclusive": { "type": "double", "isRequired": false, "isReadOnly": true, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Price excluding tax, with discount" },
"priceNetto": { "type": "double", "isRequired": false, "isReadOnly": true, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "PRICE_NETTO" },
"priceBrutto": { "type": "double", "isRequired": false, "isReadOnly": true, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "PRICE_BRUTTO" },
"quantity": { "type": "double", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Quantity" },
"discountTypeId": { "type": "integer", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Discount type" },
"discountRate": { "type": "double", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Discount value" },
"discount": { "type": "double", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Discount amount" },
"taxRate": { "type": "double", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Tax" },
"taxIncluded": { "type": "char", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Tax included in price" },
"customized": { "type": "char", "isRequired": false, "isReadOnly": true, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Modified" },
"measureCode": { "type": "integer", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Unit of measure code" },
"measureName": { "type": "string", "isRequired": false, "isReadOnly": true, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Unit of measure" },
"sort": { "type": "integer", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Sort order" },
"type": { "type": "integer", "isRequired": false, "isReadOnly": true, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "TYPE" },
"storeId": { "type": "integer", "isRequired": false, "isReadOnly": true, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "STORE_ID" },
"taxName": { "type": "string", "isRequired": false, "isReadOnly": true, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "TAX_NAME" },
"priceAccount": { "type": "double", "isReadOnly": true, "isRequired": false },
"xmlId": { "type": "string", "isReadOnly": true, "isRequired": false },
"discountSum": { "type": "double", "isRequired": false, "isReadOnly": false, "isImmutable": false, "isMultiple": false, "isDynamic": false, "title": "Discount amount", "description": "Deprecated alias of `discount`; kept so clients written against the previous field list keep working. Accepted on write, never present in row data — migrate to `discount`." }
}
}
About
id. Anidsupplied inPUT /v1/invoices/:id/productsitems does not guarantee the row's previous identifier: an unchanged row gets the sameideven without this field, while a changed row gets a new one. For targeted editing, usePATCH /v1/invoices/:id/products/:rowId.
The schema is the same for every invoice. The response does not depend on the
idin the URL: passing the ID of a nonexistent invoice returns the same schema and a200response rather than an error. This call cannot be used to check that an invoice exists —GET /v1/invoices/:idis there for that.
Error response example
403 — the key lacks the required scope:
{
"success": false,
"error": {
"code": "SCOPE_DENIED",
"message": "This endpoint requires 'crm' scope"
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| HTTP403 | CodeSCOPE_DENIED |
DescriptionAPI key lacks the crm scope |
| HTTP401 | CodeTOKEN_MISSING |
DescriptionAPI key has no configured tokens |
| HTTP429 | CodeRATE_LIMITED |
DescriptionRate 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.