[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"docs-entities\u002Fquotes":3,"docs-tabs-entities\u002Fquotes":6},{"content":4,"lastmod":5},"# Предложения\n\nУправление коммерческими предложениями CRM: создание, получение, обновление, удаление, товарные позиции.\n\nBitrix24 API: `crm.quote.*`\nСкоуп: `crm`\n\n## Операции\n\n- [Создать предложение](.\u002Fquotes\u002Fcreate.md) — `POST \u002Fv1\u002Fquotes`\n- [Список предложений](.\u002Fquotes\u002Flist.md) — `GET \u002Fv1\u002Fquotes`\n- [Получить предложение](.\u002Fquotes\u002Fget.md) — `GET \u002Fv1\u002Fquotes\u002F:id`\n- [Обновить предложение](.\u002Fquotes\u002Fupdate.md) — `PATCH \u002Fv1\u002Fquotes\u002F:id`\n- [Удалить предложение](.\u002Fquotes\u002Fdelete.md) — `DELETE \u002Fv1\u002Fquotes\u002F:id`\n- [Поиск предложений](.\u002Fquotes\u002Fsearch.md) — `POST \u002Fv1\u002Fquotes\u002Fsearch`\n- [Поля предложения](.\u002Fquotes\u002Ffields.md) — `GET \u002Fv1\u002Fquotes\u002Ffields`\n- [Агрегация предложений](.\u002Fquotes\u002Faggregate.md) — `POST \u002Fv1\u002Fquotes\u002Faggregate`\n- [Получить товары](.\u002Fquotes\u002Fproducts-get.md) — `GET \u002Fv1\u002Fquotes\u002F:id\u002Fproducts`\n- [Установить товары](.\u002Fquotes\u002Fproducts-set.md) — `PUT \u002Fv1\u002Fquotes\u002F:id\u002Fproducts`\n- [Добавить товар](.\u002Fquotes\u002Fproducts-add.md) — `POST \u002Fv1\u002Fquotes\u002F:id\u002Fproducts`\n- [Удалить товар](.\u002Fquotes\u002Fproducts-delete.md) — `DELETE \u002Fv1\u002Fquotes\u002F:id\u002Fproducts\u002F:rowId`\n- [Получить товар](.\u002Fquotes\u002Fproducts-get-single.md) — `GET \u002Fv1\u002Fquotes\u002F:id\u002Fproducts\u002F:rowId`\n- [Обновить товар](.\u002Fquotes\u002Fproducts-update.md) — `PATCH \u002Fv1\u002Fquotes\u002F:id\u002Fproducts\u002F:rowId`\n- [Поля товаров](.\u002Fquotes\u002Fproducts-fields.md) — `GET \u002Fv1\u002Fquotes\u002F:id\u002Fproducts\u002Ffields`\n","2026-06-11",{"quotes\u002Fcreate.md":7,"quotes\u002Flist.md":8,"quotes\u002Fget.md":9,"quotes\u002Fupdate.md":10,"quotes\u002Fdelete.md":11,"quotes\u002Fsearch.md":12,"quotes\u002Ffields.md":13,"quotes\u002Faggregate.md":14,"quotes\u002Fproducts-get.md":15,"quotes\u002Fproducts-set.md":16,"quotes\u002Fproducts-add.md":17,"quotes\u002Fproducts-delete.md":18,"quotes\u002Fproducts-get-single.md":19,"quotes\u002Fproducts-update.md":20,"quotes\u002Fproducts-fields.md":21},"\n## Создать предложение\n\n`POST \u002Fv1\u002Fquotes`\n\nСоздаёт новое коммерческое предложение в CRM.\n\n## Поля запроса (body)\n\n| Параметр | Тип | Описание |\n|----------|-----|---------|\n| `title` | string | Название предложения |\n| `opportunity` | number | Сумма. Чтобы сумма не пересчитывалась из товарных позиций, передайте вместе с `isManualOpportunity: true`. Принимается и алиас `amount` |\n| `isManualOpportunity` | boolean | `true` — ручной режим суммы (обязателен, если задаёте `opportunity`\u002F`amount` напрямую). Битрикс24 хранит флаг как `Y`\u002F`N`; булево значение преобразуется автоматически (строка `\"Y\"` тоже принимается) |\n| `currencyId` | string | Валюта. Список: `GET \u002Fv1\u002Fcurrencies` |\n| `stageId` | string | Статус: `DRAFT`, `SENT`, `APPROVED`. Список: `GET \u002Fv1\u002Fstatuses?filter[entityId]=QUOTE_STATUS` |\n| `dealId` | number | ID сделки. Поиск: `GET \u002Fv1\u002Fdeals` |\n| `contactId` | number | ID контакта. Поиск: `GET \u002Fv1\u002Fcontacts` |\n| `companyId` | number | ID компании. Поиск: `GET \u002Fv1\u002Fcompanies` |\n| `assignedById` | number | ID ответственного. Список сотрудников: `GET \u002Fv1\u002Fusers` |\n| `comments` | string | Комментарий |\n| `begindate` | datetime | Дата начала (имя поля в нижнем регистре — как в ответах API) |\n| `closedate` | datetime | Дата закрытия |\n\n> **Имена полей при записи.** Канонические имена — те, что возвращает API: `opportunity`, `currencyId`, `begindate`, `closedate`. Их camelCase-алиасы `amount` \u002F `currency` \u002F `beginDate` \u002F `closeDate` **тоже принимаются** на запись — слой API переводит их в канонические перед вызовом Битрикс24 (проверено живым вызовом: `amount: 777` → сохранено в `opportunity`). Раньше алиасы молча терялись — исправлено (VB-2992cbe0).\n\nПолный список полей: [GET \u002Fv1\u002Fquotes\u002Ffields](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields). Пользовательские поля (`ufCrm_*`) также принимаются.\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X POST https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"title\": \"КП на серверное оборудование\",\n    \"opportunity\": 150000,\n    \"isManualOpportunity\": true,\n    \"currencyId\": \"RUB\",\n    \"stageId\": \"DRAFT\",\n    \"dealId\": 741,\n    \"assignedById\": 1\n  }'\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X POST https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"title\": \"КП на серверное оборудование\",\n    \"opportunity\": 150000,\n    \"isManualOpportunity\": true,\n    \"currencyId\": \"RUB\",\n    \"stageId\": \"DRAFT\",\n    \"dealId\": 741,\n    \"assignedById\": 1\n  }'\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    title: 'КП на серверное оборудование',\n    opportunity: 150000,\n    isManualOpportunity: true,\n    currencyId: 'RUB',\n    stageId: 'DRAFT',\n    dealId: 741,\n    assignedById: 1,\n  }),\n})\n\nconst { success, data } = await res.json()\nconsole.log('Quote ID:', data.id)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    title: 'КП на серверное оборудование',\n    opportunity: 150000,\n    isManualOpportunity: true,\n    currencyId: 'RUB',\n    stageId: 'DRAFT',\n    dealId: 741,\n    assignedById: 1,\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `id` | number | ID созданного предложения |\n| `title` | string | Название |\n| `opportunity` | number | Сумма |\n| `currencyId` | string | Валюта |\n| `stageId` | string | Статус |\n| `dealId` | number | ID сделки |\n| `assignedById` | number | Ответственный |\n| `createdBy` | number | Создатель |\n| `createdTime` | datetime | Дата создания |\n| `updatedTime` | datetime | Дата изменения |\n\nОтвет содержит все поля предложения, включая пользовательские (`ufCrm_*`). Выше показаны основные.\n\nURL карточки предложения в Битрикс24 строится из `id`:\n\n```\nhttps:\u002F\u002F\u003Cportal>.bitrix24.ru\u002Fcrm\u002Ftype\u002F7\u002Fdetails\u002F\u003Cid>\u002F\n```\n\n`7` — `entityTypeId` коммерческого предложения в Битрикс24. `\u003Cportal>` — домен портала. Доступ ограничен правами сотрудника в Битрикс24.\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 412,\n    \"title\": \"КП на серверное оборудование\",\n    \"opportunity\": 150000,\n    \"isManualOpportunity\": true,\n    \"currencyId\": \"RUB\",\n    \"stageId\": \"DRAFT\",\n    \"dealId\": 741,\n    \"contactId\": 0,\n    \"companyId\": 0,\n    \"assignedById\": 1,\n    \"createdBy\": 1,\n    \"comments\": \"\",\n    \"begindate\": \"2026-04-15T00:00:00.000Z\",\n    \"closedate\": \"2026-05-15T00:00:00.000Z\",\n    \"createdTime\": \"2026-04-15T10:30:00.000Z\",\n    \"updatedTime\": \"2026-04-15T10:30:00.000Z\"\n  }\n}\n```\n\n## Пример ответа при ошибке\n\n403 — нет скоупа:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"SCOPE_DENIED\",\n    \"message\": \"This endpoint requires 'crm' scope\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n| 400 | `INVALID_REQUEST` | Невалидные поля |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Работа с файлами в полях CRM](\u002Fdocs\u002Frecipes\u002Fcrm-files)\n- [Список предложений](\u002Fdocs\u002Fentities\u002Fquotes\u002Flist) — получение с фильтрами\n- [Поля предложения](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields) — полный список полей\n- [Entity API](\u002Fdocs\u002Fentity-api) — общие принципы (пагинация, select, order)\n- [Синтаксис фильтрации](\u002Fdocs\u002Ffiltering)\n- [Batch](\u002Fdocs\u002Fbatch) — объединение операций\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Список предложений\n\n`GET \u002Fv1\u002Fquotes`\n\nВозвращает список коммерческих предложений с поддержкой фильтрации, сортировки и авто-пагинации.\n\n## Параметры\n\n| Параметр | Тип | По умолч. | Описание |\n|----------|-----|-----------|---------|\n| `limit` | number | `50` | Количество записей (до 5000). При `limit > 50` Вайбкод автоматически запрашивает несколько страниц у B24 |\n| `offset` | number | `0` | Пропустить N записей. При `offset > 0` рекомендуется `limit \u003C= 500` |\n| `select` | string | — | Выборка полей: `?select=id,title,amount` |\n| `order` | object | — | Сортировка: `?order[createdTime]=desc` |\n| `filter` | object | — | Фильтрация по полям `GET \u002Fv1\u002Fquotes\u002Ffields`.\u003Cbr>[Синтаксис фильтрации](\u002Fdocs\u002Ffiltering). Пример: `?filter[stageId]=DRAFT` |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes?limit=10&order[amount]=desc&filter[stageId]=DRAFT\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes?limit=10&order[amount]=desc&filter[stageId]=DRAFT\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes?limit=10&order[amount]=desc&filter[stageId]=DRAFT', {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data, total } = await res.json()\nconsole.log(`Найдено ${total} предложений`)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes?limit=10&order[amount]=desc&filter[stageId]=DRAFT', {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data, total } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `data` | array | Массив предложений (каждое содержит все поля — см. [Поля](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields)) |\n| `total` | number | Общее количество записей, соответствующих фильтру |\n| `page` | number | Текущая страница |\n| `limit` | number | Размер страницы |\n\nURL карточки любого предложения из массива `data` — его `id`:\n\n```\nhttps:\u002F\u002F\u003Cportal>.bitrix24.ru\u002Fcrm\u002Ftype\u002F7\u002Fdetails\u002F\u003Cid>\u002F\n```\n\n`7` — `entityTypeId` коммерческого предложения в Битрикс24. `\u003Cportal>` — домен портала. Доступ ограничен правами сотрудника в Битрикс24.\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"id\": 412,\n      \"title\": \"КП на серверное оборудование\",\n      \"amount\": 150000,\n      \"currency\": \"RUB\",\n      \"stageId\": \"DRAFT\",\n      \"dealId\": 741,\n      \"assignedById\": 1,\n      \"createdTime\": \"2026-04-15T10:30:00.000Z\",\n      \"contactId\": 71,\n      \"companyId\": 15\n    }\n  ],\n  \"total\": 48,\n  \"page\": 1,\n  \"limit\": 10\n}\n```\n\n## Пример ответа при ошибке\n\n403 — нет скоупа:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"SCOPE_DENIED\",\n    \"message\": \"This endpoint requires 'crm' scope\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Известные особенности\n\n**Авто-пагинация:** при `limit > 50` Вайбкод автоматически запрашивает несколько страниц у Bitrix24 и возвращает все записи в одном ответе.\n\n**Ограничение offset:** при `offset >= 2500` Bitrix24 может вернуть `INTERNAL_ERROR`. Используйте `limit \u003C= 500` при больших offset.\n\n**Когда использовать search вместо list:** для сложных фильтров с множеством условий удобнее `POST \u002Fv1\u002Fquotes\u002Fsearch` — параметры передаются в body, а не в query-строке. Также search поддерживает [оконный поиск по датам](\u002Fdocs\u002Fbatch) для больших выборок. См. [Поиск предложений](\u002Fdocs\u002Fentities\u002Fquotes\u002Fsearch).\n\n## Смотрите также\n\n- [Поиск предложений](\u002Fdocs\u002Fentities\u002Fquotes\u002Fsearch) — POST-запрос для сложных фильтров\n- [Создать предложение](\u002Fdocs\u002Fentities\u002Fquotes\u002Fcreate) — создание нового\n- [Синтаксис фильтрации](\u002Fdocs\u002Ffiltering)\n- [Entity API](\u002Fdocs\u002Fentity-api) — select, order, пагинация\n- [Batch](\u002Fdocs\u002Fbatch) — объединение нескольких list-запросов\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Получить предложение\n\n`GET \u002Fv1\u002Fquotes\u002F:id`\n\nВозвращает коммерческое предложение по ID со всеми полями, включая пользовательские (`ufCrm_*`).\n\n## Параметры\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | да | ID предложения |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412', {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data } = await res.json()\nconsole.log('Предложение:', data.title, '—', data.amount, data.currency)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412', {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data } = await res.json()\n```\n\nПодробнее об include: [Связанные данные](\u002Fdocs\u002Fincludes).\n\n## Поля ответа\n\nОбъект предложения со всеми полями — см. [Поля предложения](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields).\n\n## Связанные данные\n\nПолучить связанные сущности вместе с предложением — параметр `include` в GET-запросе:\n\n```\nGET \u002Fv1\u002Fquotes\u002F412?include=deal,contact,company\n```\n\nДоступные include: `deal`, `contact`, `company`. Результат в поле `_included`:\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 412,\n    \"title\": \"КП на серверное оборудование\",\n    \"_included\": {\n      \"deal\": { \"id\": 741, \"title\": \"Поставка оборудования\", \"amount\": 50000 },\n      \"contact\": { \"id\": 71, \"name\": \"Иван Петров\" },\n      \"company\": { \"id\": 15, \"title\": \"ООО Ромашка\" }\n    }\n  }\n}\n```\n\nПодробнее об include: [Связанные данные](\u002Fdocs\u002Fincludes).\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 412,\n    \"title\": \"КП на серверное оборудование\",\n    \"amount\": 150000,\n    \"currency\": \"RUB\",\n    \"stageId\": \"SENT\",\n    \"dealId\": 741,\n    \"contactId\": 71,\n    \"companyId\": 15,\n    \"assignedById\": 29,\n    \"createdBy\": 1,\n    \"comments\": \"\",\n    \"beginDate\": \"2026-04-15T00:00:00.000Z\",\n    \"closeDate\": \"2026-05-15T00:00:00.000Z\",\n    \"createdTime\": \"2026-04-15T10:30:00.000Z\",\n    \"updatedTime\": \"2026-04-15T14:22:00.000Z\",\n    \"_included\": {\n      \"deal\": { \"id\": 741, \"title\": \"Поставка оборудования\", \"amount\": 50000 },\n      \"contact\": { \"id\": 71, \"name\": \"Иван Петров\" },\n      \"company\": { \"id\": 15, \"title\": \"ООО Ромашка\" }\n    }\n  }\n}\n```\n\n\n## Пример ответа при ошибке\n\n404 — предложение не найдено:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\nПодробнее об include: [Связанные данные](\u002Fdocs\u002Fincludes).\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | Предложение с таким ID не найдено |\n| 403 | `ACCESS_DENIED` | Нет доступа к предложению |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Работа с файлами в полях CRM](\u002Fdocs\u002Frecipes\u002Fcrm-files)\n- [Обновить предложение](\u002Fdocs\u002Fentities\u002Fquotes\u002Fupdate) — изменение полей\n- [Список предложений](\u002Fdocs\u002Fentities\u002Fquotes\u002Flist) — поиск с фильтрами\n- [Поля предложения](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields) — описание всех полей\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Обновить предложение\n\n`PATCH \u002Fv1\u002Fquotes\u002F:id`\n\nОбновляет поля существующего коммерческого предложения. Передайте только изменяемые поля. Полный список в [справочнике полей](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields), включая пользовательские (`ufCrm_*`).\n\n## Часто обновляемые поля\n\n| Параметр | Тип | Описание |\n|----------|-----|---------|\n| `stageId` | string | Статус: `DRAFT`, `SENT`, `APPROVED`. Список: `GET \u002Fv1\u002Fstatuses?filter[entityId]=QUOTE_STATUS` |\n| `opportunity` | number | Сумма предложения. Передавайте вместе с `isManualOpportunity: true`, иначе Битрикс24 пересчитает сумму из товарных позиций (флаг хранится как `Y`\u002F`N`, булево преобразуется автоматически) |\n| `assignedById` | number | Ответственный. Список: `GET \u002Fv1\u002Fusers` |\n| `title` | string | Название |\n| `closedate` | datetime | Дата закрытия (имя поля в нижнем регистре — как в ответах API) |\n\n> **Важно:** слой записи принимает только имена полей из ответов API: `opportunity`, `currencyId`, `begindate`, `closedate`. Алиасы `amount` \u002F `currency` \u002F `beginDate` \u002F `closeDate` при записи молча игнорируются Битрикс24 — запрос вернёт 200, но значение не изменится.\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X PATCH \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"stageId\": \"APPROVED\",\n    \"isManualOpportunity\": true,\n    \"opportunity\": 145000\n  }'\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X PATCH \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"stageId\": \"APPROVED\",\n    \"isManualOpportunity\": true,\n    \"opportunity\": 145000\n  }'\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412', {\n  method: 'PATCH',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    stageId: 'APPROVED',\n    isManualOpportunity: true,\n    opportunity: 145000,\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412', {\n  method: 'PATCH',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    stageId: 'APPROVED',\n    isManualOpportunity: true,\n    opportunity: 145000,\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `data` | object | Обновлённый объект предложения со всеми полями — см. [Поля](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields) |\n\nОбновлённый объект предложения со всеми полями — см. [Поля предложения](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields).\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 412,\n    \"title\": \"КП на серверное оборудование\",\n    \"opportunity\": 145000,\n    \"currencyId\": \"RUB\",\n    \"stageId\": \"APPROVED\",\n    \"dealId\": 741,\n    \"assignedById\": 1,\n    \"updatedTime\": \"2026-04-15T15:10:00.000Z\"\n  }\n}\n```\n\n## Пример ответа при ошибке\n\n404 — предложение не найдено:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | Предложение не найдено |\n| 403 | `ACCESS_DENIED` | Нет доступа к предложению |\n| 400 | `INVALID_REQUEST` | Невалидные поля |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Работа с файлами в полях CRM](\u002Fdocs\u002Frecipes\u002Fcrm-files)\n- [Получить предложение](\u002Fdocs\u002Fentities\u002Fquotes\u002Fget) — текущие значения полей\n- [Поля предложения](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields) — какие поля можно изменять\n- [Batch](\u002Fdocs\u002Fbatch) — массовое обновление предложений\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Удалить предложение\n\n`DELETE \u002Fv1\u002Fquotes\u002F:id`\n\nУдаляет коммерческое предложение по ID. Восстановить удалённое предложение через API нельзя — создавайте новое при необходимости.\n\n## Параметры\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | да | ID предложения |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X DELETE \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X DELETE \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412', {\n  method: 'DELETE',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nif (res.status === 204) {\n  console.log('Предложение удалено')\n}\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412', {\n  method: 'DELETE',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nif (res.status === 204) {\n  console.log('Удалено')\n}\n```\n\n## Ответ\n\nПри успешном удалении возвращается HTTP-статус `204 No Content` с пустым телом — признак успеха проверяется по статусу.\n\n## Пример ответа\n\n```\nHTTP\u002F1.1 204 No Content\n```\n\n## Пример ответа при ошибке\n\n404 — предложение не найдено:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | Предложение не найдено |\n| 403 | `ACCESS_DENIED` | Нет доступа к предложению |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Список предложений](\u002Fdocs\u002Fentities\u002Fquotes\u002Flist) — найти предложение перед удалением\n- [Batch](\u002Fdocs\u002Fbatch) — массовое удаление\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Поиск предложений\n\n`POST \u002Fv1\u002Fquotes\u002Fsearch`\n\nПоиск коммерческих предложений с фильтрами и авто-пагинацией. Аналогичен `GET \u002Fv1\u002Fquotes` с фильтрами, но через POST — удобнее для сложных запросов с большим количеством условий.\n\n## Поля запроса (body)\n\n| Параметр | Тип | По умолч. | Описание |\n|----------|-----|-----------|---------|\n| `filter` | object | — | Фильтрация по полям `GET \u002Fv1\u002Fquotes\u002Ffields`.\u003Cbr>[Синтаксис фильтрации](\u002Fdocs\u002Ffiltering). Пример: `?filter[stageId]=DRAFT` |\n| `limit` | number | `50` | Количество записей (до 5000) |\n| `offset` | number | `0` | Пропустить N записей |\n| `order` | object | — | Сортировка: `{ \"createdTime\": \"desc\" }` |\n| `select` | string[] | — | Выборка полей: `[\"id\", \"title\", \"amount\"]` |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Fsearch\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"filter\": { \"stageId\": \"DRAFT\" },\n    \"limit\": 10,\n    \"order\": { \"amount\": \"desc\" }\n  }'\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Fsearch\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"filter\": { \"stageId\": \"DRAFT\" },\n    \"limit\": 10,\n    \"order\": { \"amount\": \"desc\" }\n  }'\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Fsearch', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    filter: { stageId: 'DRAFT' },\n    limit: 10,\n    order: { amount: 'desc' },\n  }),\n})\n\nconst { success, data } = await res.json()\nconsole.log('Найдено:', data.length)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Fsearch', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    filter: { stageId: 'DRAFT' },\n    limit: 10,\n    order: { amount: 'desc' },\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `data` | array | Массив предложений (все поля — см. [Поля](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields)) |\n\nURL карточки любого предложения из массива `data` — его `id`:\n\n```\nhttps:\u002F\u002F\u003Cportal>.bitrix24.ru\u002Fcrm\u002Ftype\u002F7\u002Fdetails\u002F\u003Cid>\u002F\n```\n\n`7` — `entityTypeId` коммерческого предложения в Битрикс24. `\u003Cportal>` — домен портала. Доступ ограничен правами сотрудника в Битрикс24.\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"id\": 412,\n      \"title\": \"КП на серверное оборудование\",\n      \"amount\": 150000,\n      \"currency\": \"RUB\",\n      \"stageId\": \"DRAFT\",\n      \"dealId\": 741,\n      \"assignedById\": 29,\n      \"createdTime\": \"2026-04-15T10:30:00.000Z\"\n    }\n  ]\n}\n```\n\n## Пример ответа при ошибке\n\n403 — нет скоупа:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"SCOPE_DENIED\",\n    \"message\": \"This endpoint requires 'crm' scope\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Список предложений](\u002Fdocs\u002Fentities\u002Fquotes\u002Flist) — GET-запрос с query-параметрами (для простых фильтров)\n- [Синтаксис фильтрации](\u002Fdocs\u002Ffiltering)\n- [Batch](\u002Fdocs\u002Fbatch) — объединение нескольких search в один запрос\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Поля предложения\n\n`GET \u002Fv1\u002Fquotes\u002Ffields`\n\nВозвращает полный список доступных полей, включая пользовательские (`ufCrm_*`).\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Ffields\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Ffields\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Ffields', {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data } = await res.json()\nconsole.log('Полей:', Object.keys(data).length)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Ffields', {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Битрикс24 | Тип | RO | Описание |\n|------|----------|-----|:--:|---------|\n| `id` | `id` | number | да | ID предложения |\n| `quoteNumber` | `quoteNumber` | string | | № предложения |\n| `title` | `title` | string | | Название |\n| `amount` | `opportunity` | number | | Сумма |\n| `taxValue` | `taxValue` | number | | Сумма налога |\n| `currency` | `currencyId` | string | | Валюта. Список: `GET \u002Fv1\u002Fcurrencies` |\n| `stageId` | `stageId` | string | | Статус: `DRAFT`, `SENT`, `APPROVED`. Список: `GET \u002Fv1\u002Fstatuses?filter[entityId]=QUOTE_STATUS` |\n| `isManualOpportunity` | `isManualOpportunity` | boolean | | Ручной режим суммы (`Y`\u002F`N` в Битрикс24, преобразуется в булево) |\n| `opened` | `opened` | boolean | | Доступно для всех |\n| `closed` | `closed` | boolean | да | Предложение закрыто |\n| `dealId` | `dealId` | number | | ID сделки. Поиск: `GET \u002Fv1\u002Fdeals` |\n| `leadId` | `leadId` | number | | ID лида. Поиск: `GET \u002Fv1\u002Fleads` |\n| `contactId` | `contactId` | number | | ID контакта. Поиск: `GET \u002Fv1\u002Fcontacts` |\n| `contactIds` | `contactIds` | array | да | ID контактов предложения (привязка меняется не через `crm.item.update`) |\n| `contacts` | `contacts` | array | да | Контакты предложения |\n| `companyId` | `companyId` | number | | ID компании. Поиск: `GET \u002Fv1\u002Fcompanies` |\n| `mycompanyId` | `mycompanyId` | number | | Реквизиты вашей компании |\n| `personTypeId` | `personTypeId` | number | да | Тип плательщика |\n| `assignedById` | `assignedById` | number | | Ответственный. Список: `GET \u002Fv1\u002Fusers` |\n| `createdBy` | `createdBy` | number | да | Создатель. Поиск: `GET \u002Fv1\u002Fusers` |\n| `updatedBy` | `updatedBy` | number | да | Кем обновлено |\n| `lastActivityBy` | `lastActivityBy` | number | | Автор последней активности |\n| `comments` | `comments` | string | | Комментарий |\n| `content` | `content` | string | | Содержание |\n| `terms` | `terms` | string | | Условия |\n| `locationId` | `locationId` | string | | Местоположение |\n| `webformId` | `webformId` | number | | Создано CRM-формой |\n| `storageTypeId` | `storageTypeId` | number | | Тип хранилища файлов |\n| `storageElementIds` | `storageElementIds` | array | | ID файлов в хранилище |\n| `utmSource` | `utmSource` | string | | UTM Source |\n| `utmMedium` | `utmMedium` | string | | UTM Medium |\n| `utmCampaign` | `utmCampaign` | string | | UTM Campaign |\n| `utmContent` | `utmContent` | string | | UTM Content |\n| `utmTerm` | `utmTerm` | string | | UTM Term |\n| `beginDate` | `begindate` | datetime | | Дата начала |\n| `closeDate` | `closedate` | datetime | | Дата закрытия |\n| `actualDate` | `actualDate` | datetime | | Актуально до |\n| `lastActivityTime` | `lastActivityTime` | datetime | | Последняя активность |\n| `lastCommunicationTime` | `lastCommunicationTime` | string | да | Дата последней коммуникации |\n| `lastCommunicationCallTime` | `lastCommunicationCallTime` | datetime | да | Дата последнего звонка |\n| `lastCommunicationEmailTime` | `lastCommunicationEmailTime` | datetime | да | Дата последнего e-mail |\n| `lastCommunicationImolTime` | `lastCommunicationImolTime` | datetime | да | Дата последнего диалога в открытой линии |\n| `lastCommunicationWebformTime` | `lastCommunicationWebformTime` | datetime | да | Дата последнего заполнения CRM-формы |\n| `createdTime` | `createdTime` | datetime | да | Дата создания |\n| `updatedTime` | `updatedTime` | datetime | да | Дата изменения |\n\n**Пользовательские поля** (`ufCrm_*`) также возвращаются в ответах и принимаются при создании\u002Fобновлении.\n\n\n\n## Доступные include\n\nЭндпоинт `GET \u002Fv1\u002Fquotes\u002Ffields` возвращает список доступных include: `deal`, `contact`, `company`.\n\nПример использования: [Получить quotes](\u002Fdocs\u002Fentities\u002Fquotes\u002Fget#связанные-данные).\n\nПодробнее об include: [Связанные данные](\u002Fdocs\u002Fincludes).\n\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"fields\": {\n      \"id\": { \"type\": \"number\", \"readonly\": true },\n      \"title\": { \"type\": \"string\", \"readonly\": false },\n      \"assignedById\": { \"type\": \"number\", \"readonly\": false }\n    },\n    \"batch\": [\"create\", \"update\", \"delete\"]\n  }\n}\n```\n\nПоказаны 3 из множества полей. Полный список в таблице выше.\n\n## Пример ответа при ошибке\n\n404 — не найден:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Создать предложение](\u002Fdocs\u002Fentities\u002Fquotes\u002Fcreate) — какие поля передавать\n- [Пользовательские поля](\u002Fdocs\u002Fuserfields) — создание и управление `ufCrm_*`\n- [Entity API](\u002Fdocs\u002Fentity-api) — select для выборки нужных полей\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Агрегация предложений\n\n`POST \u002Fv1\u002Fquotes\u002Faggregate`\n\nПодсчёт количества, сумма, среднее, минимум и максимум по коммерческим предложениям с фильтрацией и группировкой.\n\n**Стандартные поля:**\n\n- `amount` — сумма предложения (числовое агрегирование имеет смысл)\n- `stageId` — стадия (для `groupBy`)\n- `assignedById` — ответственный (для `groupBy`)\n\n**Пользовательские поля (UF):** UF-поля типов `integer`, `double`, `money` — для числовых функций; UF любого типа — для `groupBy`. Полный список UF-полей конкретного портала приходит в тексте ошибки `INVALID_PARAMS`, если передать несуществующее имя.\n\n## Поля запроса (body)\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `aggregate` | array | нет | Массив агрегаций. Каждый элемент: `{ \"field\": \"amount\", \"function\": \"sum\" }`. Функции: `count`, `sum`, `avg`, `min`, `max`. Для `count` поле — `\"*\"`. Без массива — только `count` |\n| `filter` | object | нет | Фильтрация по полям `GET \u002Fv1\u002Fquotes\u002Ffields`. [Синтаксис фильтрации](\u002Fdocs\u002Ffiltering) |\n| `groupBy` | string \\| string[] | нет | Поле или массив полей для группировки (максимум 5). Допустимые значения — из списка выше |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Faggregate\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"aggregate\": [\n      { \"field\": \"amount\", \"function\": \"sum\" },\n      { \"field\": \"amount\", \"function\": \"avg\" }\n    ],\n    \"filter\": { \"assignedById\": 1 },\n    \"groupBy\": \"stageId\"\n  }'\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Faggregate\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"aggregate\": [\n      { \"field\": \"amount\", \"function\": \"sum\" },\n      { \"field\": \"amount\", \"function\": \"avg\" }\n    ],\n    \"filter\": { \"assignedById\": 1 },\n    \"groupBy\": \"stageId\"\n  }'\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Faggregate', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    aggregate: [\n      { field: 'amount', function: 'sum' },\n      { field: 'amount', function: 'avg' },\n    ],\n    filter: { assignedById: 1 },\n    groupBy: 'stageId',\n  }),\n})\n\nconst { success, data } = await res.json()\nconsole.log('Всего предложений:', data.count)\nconsole.log('По стадиям:', data.groups)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002Faggregate', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    aggregate: [\n      { field: 'amount', function: 'sum' },\n      { field: 'amount', function: 'avg' },\n    ],\n    filter: { assignedById: 1 },\n    groupBy: 'stageId',\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n> Для группировки по нескольким полям передайте массив: `\"groupBy\": [\"stageId\", \"assignedById\"]` (максимум 5).\n\n## Другие сценарии\n\nПодсчёт записей — `count` с полем `\"*\"`, самый быстрый запрос без выгрузки записей. Без массива `aggregate` результат тот же:\n\n```json\n{ \"aggregate\": [{ \"field\": \"*\", \"function\": \"count\" }] }\n```\n\nРабота с пользовательскими полями (UF) — `sum` по UF + группировка по другому UF:\n\n```json\n{\n  \"aggregate\": [{ \"field\": \"UF_CRM_DISCOUNT\", \"function\": \"sum\" }],\n  \"groupBy\": \"UF_CRM_PRIORITY\"\n}\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `success` | boolean | Всегда `true` при успехе |\n| `data.count` | number | Общее количество записей под фильтр |\n| `data.aggregates` | object | Результаты агрегаций: `{ \"amount\": { \"sum\": 90000, \"avg\": 2500 } }` |\n| `data.groups` | array | Группы (только при `groupBy`). Каждый элемент: поля группировки + `count` + `aggregates` |\n| `data.meta.totalRecords` | number | Общее количество записей под фильтр |\n| `data.meta.recordsProcessed` | number | Сколько записей обработано для числовых агрегаций (максимум 5000) |\n| `data.meta.truncated` | boolean | `true`, если под фильтр попало больше 5000 записей |\n\n## Пример ответа\n\nОтвет на основной запрос (агрегации + `groupBy: \"stageId\"`):\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"count\": 36,\n    \"aggregates\": {\n      \"amount\": { \"sum\": 90000, \"avg\": 2500 }\n    },\n    \"groups\": [\n      {\n        \"stageId\": \"DRAFT\",\n        \"count\": 20,\n        \"aggregates\": { \"amount\": { \"sum\": 50000 } }\n      },\n      {\n        \"stageId\": \"APPROVED\",\n        \"count\": 16,\n        \"aggregates\": { \"amount\": { \"sum\": 40000 } }\n      }\n    ],\n    \"meta\": {\n      \"totalRecords\": 36,\n      \"recordsProcessed\": 36,\n      \"truncated\": false\n    }\n  }\n}\n```\n\nБез `groupBy` поле `data.groups` в ответе отсутствует.\n\n## Пример ответа при ошибке\n\n400 — неверное имя функции, несуществующее поле или `groupBy` по неаггрегируемому полю:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_PARAMS\",\n    \"message\": \"Field 'foo' not found. Available numeric fields: amount, stageId, assignedById\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 400 | `INVALID_PARAMS` | Невалидное имя функции, несуществующее поле, нечисловое поле в `sum`\u002F`avg`\u002F`min`\u002F`max`, `groupBy` по неаггрегируемому полю или больше 5 полей в `groupBy` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Известные особенности\n\n**`count` vs числовые функции.** `count` считается одним вызовом в Битрикс24 на любом объёме данных. Функции `sum`\u002F`avg`\u002F`min`\u002F`max` подгружают записи постранично (максимум 5000) и считают на стороне Вайбкод — если под фильтр попадает больше 5000 записей, `meta.truncated` будет `true`, агрегация выполнится по первым 5000. Для точных счётчиков на больших выборках используйте `count` или сужайте фильтр.\n\n**Money-поля.** UF-поля типа `money` хранятся в формате `\"сумма|валюта\"` (`\"1500|RUB\"`) — агрегат извлекает числовую часть автоматически, складывать можно без парсинга.\n\n**Фильтрация по UF работает.** В `filter` можно передавать любые поля — стандартные и пользовательские, любого типа. Например, `{ \"filter\": { \"ufCrm_1234\": \"value\" } }` вернёт количество предложений с этим значением UF.\n\n## Смотрите также\n\n- [Список предложений](\u002Fdocs\u002Fentities\u002Fquotes\u002Flist) — получить записи с фильтрацией\n- [Поиск предложений](\u002Fdocs\u002Fentities\u002Fquotes\u002Fsearch) — POST-запрос с фильтрами\n- [Синтаксис фильтрации](\u002Fdocs\u002Ffiltering)\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Товарные позиции предложения\n\n`GET \u002Fv1\u002Fquotes\u002F:id\u002Fproducts`\n\nВозвращает список товарных позиций, привязанных к предложению.\n\n## Параметры\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | да | ID предложения |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts', {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data } = await res.json()\nconsole.log('Товаров:', data.length)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts', {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `data` | array | Массив товарных позиций |\n| `data[].productId` | number | ID товара. Каталог: `GET \u002Fv1\u002Fproducts` |\n| `data[].productName` | string | Название товара |\n| `data[].price` | number | Цена за единицу |\n| `data[].quantity` | number | Количество |\n| `data[].discount` | number | Сумма скидки |\n| `data[].taxRate` | number\u002Fnull | Ставка налога (%) |\n| `data[].taxIncluded` | boolean | Налог включён в цену |\n\nПоказаны основные поля. Полный список (20 полей, включая priceAccount, measureCode и др.): [Поля товаров](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-fields).\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"productId\": 1,\n      \"productName\": \"Серверное оборудование\",\n      \"price\": 1000,\n      \"quantity\": 2,\n      \"discount\": 0,\n      \"taxRate\": null,\n      \"taxIncluded\": false\n    }\n  ]\n}\n```\n\n## Пример ответа при ошибке\n\n404 — предложение не найдено:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | Предложение не найдено |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Установить товары](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-set) — заменить товарные позиции\n- [Получить предложение](\u002Fdocs\u002Fentities\u002Fquotes\u002Fget) — основные данные предложения\n- [Товары каталога](\u002Fdocs\u002Fentities\u002Fproducts) — справочник товаров\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Установить товары предложения\n\n`PUT \u002Fv1\u002Fquotes\u002F:id\u002Fproducts`\n\nУстанавливает товарные позиции коммерческого предложения. Полностью заменяет текущий список — передайте все нужные позиции.\n\n## Поля запроса (body)\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `items` | array | да | Массив товарных позиций |\n| `items[].productId` | number | да | ID товара. Каталог: `GET \u002Fv1\u002Fproducts` |\n| `items[].price` | number | да | Цена за единицу |\n| `items[].quantity` | number | да | Количество |\n| `items[].discount` | number | нет | Сумма скидки |\n| `items[].taxRate` | number | нет | Ставка налога (%) |\n| `items[].taxIncluded` | boolean | нет | Налог включён в цену |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X PUT \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\u002Fproducts\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"items\": [\n      { \"productId\": 1, \"price\": 75000, \"quantity\": 2 },\n      { \"productId\": 5, \"price\": 10000, \"quantity\": 1, \"discount\": 1000 }\n    ]\n  }'\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X PUT \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\u002Fproducts\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"items\": [\n      { \"productId\": 1, \"price\": 75000, \"quantity\": 2 },\n      { \"productId\": 5, \"price\": 10000, \"quantity\": 1, \"discount\": 1000 }\n    ]\n  }'\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\u002Fproducts', {\n  method: 'PUT',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    items: [\n      { productId: 1, price: 75000, quantity: 2 },\n      { productId: 5, price: 10000, quantity: 1, discount: 1000 },\n    ],\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F412\u002Fproducts', {\n  method: 'PUT',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    items: [\n      { productId: 1, price: 75000, quantity: 2 },\n      { productId: 5, price: 10000, quantity: 1, discount: 1000 },\n    ],\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `data` | array | Массив установленных позиций с полями productId, productName, price, quantity, discount, taxRate, taxIncluded |\n\nМассив установленных позиций с полями `productId`, `productName`, `price`, `quantity`, `discount`, `taxRate`, `taxIncluded`.\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": [\n    {\n      \"productId\": 1,\n      \"productName\": \"Серверное оборудование\",\n      \"price\": 75000,\n      \"quantity\": 2,\n      \"discount\": 0,\n      \"taxRate\": null,\n      \"taxIncluded\": false\n    },\n    {\n      \"productId\": 5,\n      \"productName\": \"Установка и настройка\",\n      \"price\": 10000,\n      \"quantity\": 1,\n      \"discount\": 1000,\n      \"taxRate\": null,\n      \"taxIncluded\": false\n    }\n  ]\n}\n```\n\n## Пример ответа при ошибке\n\n400 — неверный формат:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_PARAMS\",\n    \"message\": \"items must be an array\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 400 | `INVALID_PARAMS` | `items` не является массивом |\n| 404 | `ENTITY_NOT_FOUND` | Предложение не найдено |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Известные особенности\n\n**Полная замена:** PUT заменяет весь список товаров. Чтобы добавить позицию — сначала получите текущие (`GET`), добавьте новую в массив, отправьте всё (`PUT`).\n\n## Смотрите также\n\n- [Товарные позиции](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-get) — получить текущий список\n- [Получить предложение](\u002Fdocs\u002Fentities\u002Fquotes\u002Fget) — основные данные предложения\n- [Товары каталога](\u002Fdocs\u002Fentities\u002Fproducts) — справочник товаров\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Добавить товар в предложение\n\n`POST \u002Fv1\u002Fquotes\u002F:id\u002Fproducts`\n\nДобавляет одну товарную позицию в предложение. В отличие от `PUT \u002Fv1\u002Fquotes\u002F:id\u002Fproducts`, не заменяет существующие позиции.\n\n## Поля запроса (body)\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | да | ID предложения |\n| `productId` | number | нет | ID товара из каталога. Если задан без `productName`, имя подставляется из каталога. Каталог: `GET \u002Fv1\u002Fproducts` |\n| `productName` | string | нет | Название товарной позиции — для произвольной строки без товара из каталога. Укажите хотя бы одно из `productId` \u002F `productName`. |\n| `price` | number | нет | Цена за единицу |\n| `quantity` | number | нет | Количество |\n| `discount` | number | нет | Сумма скидки |\n| `taxRate` | number | нет | Ставка налога (%) |\n| `taxIncluded` | boolean | нет | Налог включён в цену |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F52\u002Fproducts\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"productId\": 1, \"price\": 25000, \"quantity\": 2 }'\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F52\u002Fproducts\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"productId\": 1, \"price\": 25000, \"quantity\": 2 }'\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F52\u002Fproducts', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({ productId: 1, price: 25000, quantity: 2 }),\n})\n\nconst { success, data } = await res.json()\nconsole.log('ID строки:', data.id)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F52\u002Fproducts', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({ productId: 1, price: 25000, quantity: 2 }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `data.id` | number | ID созданной товарной строки |\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 1465\n  }\n}\n```\n\n## Пример ответа при ошибке\n\n404 — предложение не найдено:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | Предложение не найдено |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Товарные позиции](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-get) — получить текущий список\n- [Установить товары](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-set) — заменить все позиции\n- [Удалить товар](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-delete) — удалить одну позицию\n- [Товары каталога](\u002Fdocs\u002Fentities\u002Fproducts) — справочник товаров\n","\n## Удалить товар из предложения\n\n`DELETE \u002Fv1\u002Fquotes\u002F:id\u002Fproducts\u002F:rowId`\n\nУдаляет одну товарную позицию из предложения по ID строки. Восстановить удалённую позицию через API нельзя — добавляйте новую при необходимости.\n\n## Параметры\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | да | ID предложения |\n| `rowId` (path) | number | да | ID товарной строки (из ответа add или list) |\n\n`rowId` — это ID товарной строки, а не `productId` из каталога товаров.\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X DELETE \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F52\u002Fproducts\u002F1465\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X DELETE \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F52\u002Fproducts\u002F1465\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F52\u002Fproducts\u002F1465', {\n  method: 'DELETE',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nif (res.status === 204) {\n  console.log('Товар удалён из предложения')\n}\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F52\u002Fproducts\u002F1465', {\n  method: 'DELETE',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nif (res.status === 204) {\n  console.log('Удалено')\n}\n```\n\n## Ответ\n\nПри успешном удалении возвращается HTTP-статус `204 No Content` с пустым телом — признак успеха проверяется по статусу.\n\n## Пример ответа\n\n```\nHTTP\u002F1.1 204 No Content\n```\n\n## Пример ответа при ошибке\n\n404 — предложение не найдено:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | Предложение или товарная строка не найдена |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Товарные позиции](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-get) — получить текущий список\n- [Добавить товар](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-add) — добавить позицию\n- [Установить товары](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-set) — заменить все позиции\n- [Товары каталога](\u002Fdocs\u002Fentities\u002Fproducts) — справочник товаров\n","\n## Получить товар из предложения\n\n`GET \u002Fv1\u002Fquotes\u002F:id\u002Fproducts\u002F:rowId`\n\nВозвращает одну товарную позицию предложения по ID строки.\n\n## Параметры\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | да | ID предложения |\n| `rowId` (path) | number | да | ID товарной строки (из ответа add или list) |\n\n`rowId` — это ID товарной строки, а не `productId` из каталога товаров.\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X GET \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002F1471\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X GET \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002F1471\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002F1471', {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data } = await res.json()\nconsole.log('Цена:', data.price)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002F1471', {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | Описание |\n|------|-----|---------|\n| `data.id` | number | ID товарной строки |\n| `data.productId` | number | ID товара из каталога |\n| `data.productName` | string | Название товара |\n| `data.price` | number | Цена за единицу |\n| `data.quantity` | number | Количество |\n| `data.discount` | number | Сумма скидки |\n| `data.discountRate` | number | Процент скидки |\n| `data.discountTypeId` | number | Тип скидки (1 — сумма, 2 — процент) |\n| `data.taxRate` | number \\| null | Ставка налога (%) |\n| `data.taxIncluded` | boolean | Налог включён в цену |\n| `data.priceExclusive` | number | Цена без скидки |\n| `data.priceNetto` | number | Цена нетто |\n| `data.priceBrutto` | number | Цена брутто |\n| `data.priceAccount` | number | Цена в валюте учёта |\n| `data.measureCode` | number | Код единицы измерения |\n| `data.measureName` | string | Название единицы измерения |\n| `data.sort` | number | Сортировка |\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 1471,\n    \"productId\": 1,\n    \"productName\": \"День добрый!\",\n    \"price\": 5000,\n    \"priceAccount\": 5000,\n    \"priceExclusive\": 5000,\n    \"priceNetto\": 5000,\n    \"priceBrutto\": 5000,\n    \"quantity\": 3,\n    \"discountTypeId\": 2,\n    \"discountRate\": 0,\n    \"discount\": 0,\n    \"taxRate\": null,\n    \"taxIncluded\": false,\n    \"customized\": \"Y\",\n    \"measureCode\": 796,\n    \"measureName\": \"шт\",\n    \"sort\": 0,\n    \"xmlId\": \"sale_basket_995\",\n    \"type\": 1\n  }\n}\n```\n\n## Пример ответа при ошибке\n\n404 — предложение или товарная строка не найдена:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | Предложение или товарная строка не найдена |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Товарные позиции](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-get) — получить все позиции\n- [Обновить товар](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-update) — обновить позицию\n- [Добавить товар](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-add) — добавить позицию\n- [Удалить товар](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-delete) — удалить позицию\n- [Товары каталога](\u002Fdocs\u002Fentities\u002Fproducts) — справочник товаров\n","\n## Обновить товар предложения\n\n`PATCH \u002Fv1\u002Fquotes\u002F:id\u002Fproducts\u002F:rowId`\n\nОбновляет товарную позицию предложения. Передайте только изменяемые поля.\n\n## Параметры\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | да | ID предложения |\n| `rowId` (path) | number | да | ID товарной позиции (из ответа list или add, не productId из каталога) |\n\n## Поля запроса (body)\n\n| Параметр | Тип | Описание |\n|----------|-----|---------|\n| `price` | number | Цена за единицу |\n| `quantity` | number | Количество |\n| `productId` | number | ID товара. Каталог: `GET \u002Fv1\u002Fproducts` |\n| `discount` | number | Сумма скидки |\n| `taxRate` | number | Ставка налога (%) |\n| `taxIncluded` | boolean | Налог включён в цену |\n| `sort` | number | Сортировка |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl -X PATCH \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002F1471\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"price\": 9999, \"quantity\": 10 }'\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl -X PATCH \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002F1471\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{ \"price\": 9999, \"quantity\": 10 }'\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002F1471', {\n  method: 'PATCH',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({ price: 9999, quantity: 10 }),\n})\n\nconst { success, data } = await res.json()\nconsole.log('Обновлено:', data.price, 'x', data.quantity)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002F1471', {\n  method: 'PATCH',\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({ price: 9999, quantity: 10 }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\nОбновлённый объект товарной позиции: `id`, `productId`, `productName`, `price`, `quantity`, `discount`, `taxRate`, `taxIncluded`.\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": 1471,\n    \"productId\": 1,\n    \"productName\": \"Серверное оборудование\",\n    \"price\": 9999,\n    \"quantity\": 10,\n    \"discount\": 0,\n    \"taxRate\": null,\n    \"taxIncluded\": false\n  }\n}\n```\n\n## Пример ответа при ошибке\n\n404 — позиция не найдена:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | Позиция не найдена |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Получить товар](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-get-single) — получение одной позиции\n- [Получить товары](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-get) — список всех позиций\n- [Добавить товар](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-add) — добавление\n- [Лимиты и оптимизация](\u002Fdocs\u002Foptimization) — rate limits\n","\n## Поля товаров предложения\n\n`GET \u002Fv1\u002Fquotes\u002F:id\u002Fproducts\u002Ffields`\n\nВозвращает описание полей товарных позиций предложения: названия, типы, доступность для чтения и записи.\n\n## Параметры\n\n| Параметр | Тип | Обяз. | Описание |\n|----------|-----|:-----:|---------|\n| `id` (path) | number | да | ID предложения |\n\n## Примеры\n\n### curl — личный ключ\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002Ffields\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\"\n```\n\n### curl — OAuth-приложение\n\n```bash\ncurl \"https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002Ffields\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\"\n```\n\n### JavaScript — личный ключ\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002Ffields', {\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n  },\n})\n\nconst { success, data } = await res.json()\nconsole.log('Полей:', Object.keys(data).length)\n```\n\n### JavaScript — OAuth-приложение\n\n```javascript\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.tech\u002Fv1\u002Fquotes\u002F741\u002Fproducts\u002Ffields', {\n  headers: {\n    'X-Api-Key': 'YOUR_APP_KEY',\n    'Authorization': 'Bearer USER_SESSION_TOKEN',\n  },\n})\n\nconst { success, data } = await res.json()\n```\n\n## Поля ответа\n\n| Поле | Тип | RO | Обяз. | Описание |\n|------|-----|:--:|:-----:|---------|\n| `id` | integer | да | | ID позиции |\n| `productId` | integer | | да | ID товара. Каталог: `GET \u002Fv1\u002Fproducts` |\n| `productName` | string | | | Название товара |\n| `price` | double | | | Цена |\n| `quantity` | double | | | Количество |\n| `discountSum` | double | | | Сумма скидки |\n| `discountRate` | double | | | Величина скидки (%) |\n| `discountTypeId` | integer | | | Тип скидки |\n| `taxRate` | double | | | Налог (%) |\n| `taxIncluded` | char | | | Налог включён в цену (`Y`\u002F`N`) |\n| `priceExclusive` | double | да | | Цена без налога со скидкой |\n| `priceNetto` | double | да | | Цена нетто |\n| `priceBrutto` | double | да | | Цена брутто |\n| `measureCode` | integer | | | Код единицы измерения |\n| `measureName` | string | | | Единица измерения |\n| `customized` | char | | | Изменён (`Y`\u002F`N`) |\n| `sort` | integer | | | Сортировка |\n| `type` | integer | да | | Тип |\n| `storeId` | integer | да | | ID склада |\n| `ownerId` | integer | | да | ID владельца (предложения) |\n| `ownerType` | string | | да | Тип владельца |\n\n## Пример ответа\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": { \"type\": \"integer\", \"isRequired\": false, \"isReadOnly\": true, \"title\": \"ID\" },\n    \"ownerId\": { \"type\": \"integer\", \"isRequired\": true, \"isReadOnly\": false, \"isImmutable\": true, \"title\": \"ID владельца\" },\n    \"ownerType\": { \"type\": \"string\", \"isRequired\": true, \"isReadOnly\": false, \"isImmutable\": true, \"title\": \"Тип владельца\" },\n    \"productId\": { \"type\": \"integer\", \"isRequired\": true, \"isReadOnly\": false, \"title\": \"Товар\" },\n    \"productName\": { \"type\": \"string\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Название товара\" },\n    \"price\": { \"type\": \"double\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Цена\" },\n    \"priceExclusive\": { \"type\": \"double\", \"isRequired\": false, \"isReadOnly\": true, \"title\": \"Цена без налога со скидкой\" },\n    \"priceNetto\": { \"type\": \"double\", \"isRequired\": false, \"isReadOnly\": true, \"title\": \"PRICE_NETTO\" },\n    \"priceBrutto\": { \"type\": \"double\", \"isRequired\": false, \"isReadOnly\": true, \"title\": \"PRICE_BRUTTO\" },\n    \"quantity\": { \"type\": \"double\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Количество\" },\n    \"discountTypeId\": { \"type\": \"integer\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Тип скидки\" },\n    \"discountRate\": { \"type\": \"double\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Величина скидки\" },\n    \"discountSum\": { \"type\": \"double\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Сумма скидки\" },\n    \"taxRate\": { \"type\": \"double\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Налог\" },\n    \"taxIncluded\": { \"type\": \"char\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Налог включен в цену\" },\n    \"customized\": { \"type\": \"char\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Изменен\" },\n    \"measureCode\": { \"type\": \"integer\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Код единицы измерения\" },\n    \"measureName\": { \"type\": \"string\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Единица измерения\" },\n    \"sort\": { \"type\": \"integer\", \"isRequired\": false, \"isReadOnly\": false, \"title\": \"Сортировка\" },\n    \"type\": { \"type\": \"integer\", \"isRequired\": false, \"isReadOnly\": true, \"title\": \"TYPE\" },\n    \"storeId\": { \"type\": \"integer\", \"isRequired\": false, \"isReadOnly\": true, \"title\": \"STORE_ID\" }\n  }\n}\n```\n\n## Пример ответа при ошибке\n\n404 — предложение не найдено:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"ENTITY_NOT_FOUND\",\n    \"message\": \"Элемент не найден\"\n  }\n}\n```\n\n## Ошибки\n\n| HTTP | Код | Описание |\n|------|-----|---------|\n| 404 | `ENTITY_NOT_FOUND` | Предложение не найдено |\n| 403 | `SCOPE_DENIED` | API-ключ не имеет скоупа `crm` |\n| 401 | `TOKEN_MISSING` | API-ключ не имеет настроенных токенов |\n\nПолный список общих ошибок API — [Ошибки](\u002Fdocs\u002Ferrors).\n\n## Смотрите также\n\n- [Товарные позиции](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-get) — получить товары предложения\n- [Добавить товар](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-add) — добавить позицию\n- [Установить товары](\u002Fdocs\u002Fentities\u002Fquotes\u002Fproducts-set) — заменить все позиции\n- [Поля предложения](\u002Fdocs\u002Fentities\u002Fquotes\u002Ffields) — описание полей предложения\n"]