[{"data":1,"prerenderedAt":7},["ShallowReactive",2],{"docs-entities\u002Fdocuments\u002Fcrm-upload":3,"docs-tabs-entities\u002Fdocuments\u002Fcrm-upload":6},{"content":4,"lastmod":5},"## Upload a CRM document\n\n`POST \u002Fv1\u002Fcrm-documents\u002Fupload`\n\nAttaches a ready-made document file, produced outside Bitrix24 without a template, to a CRM record. The file is sent as a base64 string, and the fields go inside the `fields` object.\n\n## Request fields (body)\n\n| Field | Type | Required | Description |\n|------|-----|:-----:|---------|\n| `fields.entityTypeId` | number | yes | CRM record type, a positive integer: `2` — deal, `3` — contact, `4` — company. Other types are in the \"CRM record types\" list on the [`GET \u002Fv1\u002Fcrm-documents`](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fcrm-list) page |\n| `fields.entityId` | number | yes | Record ID, a positive integer. The source depends on the type: deals — [`GET \u002Fv1\u002Fdeals`](\u002Fdocs\u002Fentities\u002Fdeals\u002Flist), contacts — [`GET \u002Fv1\u002Fcontacts`](\u002Fdocs\u002Fentities\u002Fcontacts\u002Flist), companies — [`GET \u002Fv1\u002Fcompanies`](\u002Fdocs\u002Fentities\u002Fcompanies\u002Flist) |\n| `fields.title` | string | yes | Document title |\n| `fields.number` | string | yes | Document number. A number instead of a string is rejected |\n| `fields.region` | string | yes | Document region, for example `uk` or `de` |\n| `fields.fileContent` | string | yes | DOCX file content as a base64 string, at most 2 MiB after decoding |\n| `fields.pdfContent` | string | no | PDF version of the document as a base64 string, at most 2 MiB after decoding |\n| `fields.imageContent` | string | no | Document image as a base64 string, at most 2 MiB after decoding |\n\nThe whole request body is at most 3 MiB. The `id`, `fileId`, `pdfId`, `imageId`, `moduleId`, `providerClassName` and `value` fields are set by Bitrix24: a request with any of them is rejected with `400 READONLY_FIELD`.\n\n## Examples\n\nIn the examples, `contract.docx` is your document file. The request body is assembled into a `document.json` file and then sent.\n\n### curl — personal key\n\n```bash\nprintf '{\"fields\":{\"entityTypeId\":2,\"entityId\":8781,\"title\":\"Supply agreement\",\"number\":\"VD-1\",\"region\":\"uk\",\"fileContent\":\"%s\"}}' \\\n  \"$(base64 \u003C contract.docx | tr -d '\\n')\" > document.json\n\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcrm-documents\u002Fupload\" \\\n  -H \"X-Api-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  --data-binary @document.json\n```\n\n### curl — OAuth application\n\n```bash\nprintf '{\"fields\":{\"entityTypeId\":2,\"entityId\":8781,\"title\":\"Supply agreement\",\"number\":\"VD-1\",\"region\":\"uk\",\"fileContent\":\"%s\"}}' \\\n  \"$(base64 \u003C contract.docx | tr -d '\\n')\" > document.json\n\ncurl -X POST \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcrm-documents\u002Fupload\" \\\n  -H \"X-Api-Key: YOUR_APP_KEY\" \\\n  -H \"Authorization: Bearer USER_SESSION_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  --data-binary @document.json\n```\n\n### JavaScript — personal key\n\n```javascript\nimport { readFile } from 'node:fs\u002Fpromises'\n\nconst fileContent = (await readFile('contract.docx')).toString('base64')\n\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcrm-documents\u002Fupload', {\n  method: 'POST',\n  headers: {\n    'X-Api-Key': 'YOUR_API_KEY',\n    'Content-Type': 'application\u002Fjson',\n  },\n  body: JSON.stringify({\n    fields: {\n      entityTypeId: 2,\n      entityId: 8781,\n      title: 'Supply agreement',\n      number: 'VD-1',\n      region: 'uk',\n      fileContent,\n    },\n  }),\n})\n\nconst { success, data } = await res.json()\nconsole.log('Document:', data.id)\n```\n\n### JavaScript — OAuth application\n\n```javascript\nimport { readFile } from 'node:fs\u002Fpromises'\n\nconst fileContent = (await readFile('contract.docx')).toString('base64')\n\nconst res = await fetch('https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcrm-documents\u002Fupload', {\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    fields: {\n      entityTypeId: 2,\n      entityId: 8781,\n      title: 'Supply agreement',\n      number: 'VD-1',\n      region: 'uk',\n      fileContent,\n    },\n  }),\n})\n\nconst { success, data } = await res.json()\n```\n\n## Response fields\n\nReturns HTTP status `201 Created` and the created document. The full description of document fields is on the [Get a CRM document](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fcrm-get) page.\n\n| Field | Type | Description |\n|------|-----|---------|\n| `success` | boolean | Always `true` on success |\n| `data.id` | string | Document ID |\n| `data.title` | string | Title from `fields.title` |\n| `data.number` | string | Number from `fields.number` |\n| `data.entityTypeId` | string | CRM record type |\n| `data.entityId` | string | CRM record ID |\n| `data.templateId` | string | Internal template used for uploaded documents. It does not appear in template lists |\n| `data.createdBy` | string | ID of the user who uploaded the document. List: [`GET \u002Fv1\u002Fusers`](\u002Fdocs\u002Fentities\u002Fusers) |\n| `data.downloadUrlMachine` | string | URL of [`\u002Fv1\u002Fcrm-documents\u002F:id\u002Fdownload`](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fdownload). Requires `X-Api-Key` with the `crm` scope |\n| `data.pdfUrlMachine` | string | URL of [`\u002Fv1\u002Fcrm-documents\u002F:id\u002Fpdf`](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fpdf). Present in the response right away if `fields.pdfContent` was sent |\n\n## Response example\n\nThe main fields are shown. Full list: [Get a CRM document](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fcrm-get).\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"downloadUrl\": \"https:\u002F\u002Fexample.bitrix24.com\u002Fbitrix\u002Fservices\u002Fmain\u002Fajax.php?action=crm.documentgenerator.document.download&SITE_ID=s1&id=1985\",\n    \"publicUrl\": null,\n    \"title\": \"Supply agreement\",\n    \"number\": \"VD-1\",\n    \"id\": \"1985\",\n    \"createTime\": \"2026-10-08T17:59:45+00:00\",\n    \"createdBy\": \"1317\",\n    \"stampsEnabled\": false,\n    \"isTransformationError\": false,\n    \"templateId\": \"275\",\n    \"entityId\": \"8781\",\n    \"entityTypeId\": \"2\",\n    \"downloadUrlMachine\": \"https:\u002F\u002Fvibecode.bitrix24.com\u002Fv1\u002Fcrm-documents\u002F1985\u002Fdownload\"\n  }\n}\n```\n\n## Error response example\n\n400 — `fileContent` is not a base64 string:\n\n```json\n{\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_PARAMS\",\n    \"message\": \"fileContent must be base64\"\n  }\n}\n```\n\n## Errors\n\n| HTTP | Code | Description |\n|------|-----|---------|\n| 400 | `MISSING_REQUIRED_FIELDS` | The `fields` object is missing. Message: `fields is required` |\n| 400 | `MISSING_REQUIRED_FIELDS` | A required field is missing or empty. The message names the fields: `Empty required fields: number` |\n| 400 | `READONLY_FIELD` | `fields` contains a field set by Bitrix24, for example `id` |\n| 400 | `INVALID_PARAMS` | `entityTypeId` or `entityId` is not a positive integer |\n| 400 | `INVALID_PARAMS` | `title`, `number` or `region` is not a string |\n| 400 | `INVALID_PARAMS` | `fileContent`, `pdfContent` or `imageContent` is not a base64 string. The message names the field |\n| 400 | `INVALID_PARAMS` | The CRM record type in `entityTypeId` is not supported for documents, for example `999`. Message: `Wrong \"entityTypeId\" field value` |\n| 413 | `PAYLOAD_TOO_LARGE` | A file exceeds 2 MiB after decoding. The message names the field: `fileContent must not exceed 2 MiB` |\n| 413 | `PAYLOAD_TOO_LARGE` | The request body exceeds 3 MiB. Message: `Request body too large` |\n| 429 | `LARGE_BODY_BACKEND_BUSY` | The body exceeds 1 MiB and the platform is already processing the maximum volume of large bodies. Retry the request after `Retry-After` seconds |\n| 502 | `BITRIX_INVALID_RESPONSE` | Bitrix24 responded without a document |\n| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key is read-only |\n| 403 | `SCOPE_DENIED` | The key lacks the `crm` scope |\n| 401 | `MISSING_API_KEY` | The `X-Api-Key` header is missing |\n| 401 | `TOKEN_MISSING` | No tokens are mapped to the key — an OAuth application key was called without a user session |\n\nFull list of common API errors — [Errors](\u002Fdocs\u002Ferrors).\n\n## Known specifics\n\n**The file format is not checked.** The only check is that `fileContent` is a valid base64 string. The file is stored as sent, even if it is not a DOCX, and [the DOCX download](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fdownload) returns the same bytes.\n\n**The record's existence is not checked.** A document is created even for an `entityId` that does not exist among records of this type: the response is `201`, including for a contact — unlike [creating from a template](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fcrm-create). A document attached to a non-existent contact cannot be read or deleted afterwards: all methods return `403 BITRIX_ACCESS_DENIED`. Check the record beforehand, for example with [`GET \u002Fv1\u002Fcontacts\u002F:id`](\u002Fdocs\u002Fentities\u002Fcontacts\u002Fget).\n\n**Without `pdfContent`, the PDF is generated later.** If no PDF version is sent, the PDF is generated from the DOCX after the upload, and until then the [PDF](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fpdf) download returns `409 DOCUMENT_NOT_READY`.\n\n## See also\n\n- [Create a CRM document](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fcrm-create)\n- [Get a CRM document](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fcrm-get)\n- [Documents of a CRM entity](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fcrm-list)\n- [Download document DOCX](\u002Fdocs\u002Fentities\u002Fdocuments\u002Fdownload)\n- [Documents](\u002Fdocs\u002Fentities\u002Fdocuments)\n","2026-10-09",{},1794739374429]