For AI agents: markdown of this page — /docs-content-en/feedback/attachments.md documentation index — /llms.txt

Upload an attachment

POST /v1/feedback/attachments

Uploads an image or a document and returns its ID for later linking to a ticket or comment. The upload is two-step: first the file is sent here, then the returned id is passed in the attachmentIds field when creating a ticket or adding a comment.

The file is passed as multipart/form-data in the file field; its type is determined by the extension in the file name. PNG, JPEG, WebP, and GIF images are re-encoded on the server: PNG, JPEG, and WebP are converted to WebP, and GIF is re-encoded as GIF, preserving animation. TXT, MD, CSV, HTML, XLSX, and ZIP documents are stored unchanged. Limits: up to 10 MB per file, up to 24 million pixels per image, up to 5 attachments per message and up to 25 per ticket.

Request fields (multipart/form-data)

Field Type Required Description
file file yes A file up to 10 MB. An image — extension .png, .jpg, .jpeg, .webp, or .gif. A document — .txt, .md, .csv, .html, .htm, .xlsx, or .zip

Examples

curl — personal key

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/feedback/attachments \
  -H "X-Api-Key: YOUR_API_KEY" \
  -F "file=@screenshot.png"

curl — OAuth application

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/feedback/attachments \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -F "file=@screenshot.png"

JavaScript — personal key

javascript
const form = new FormData()
form.append('file', fileInput.files[0])

const res = await fetch('https://vibecode.bitrix24.com/v1/feedback/attachments', {
  method: 'POST',
  headers: { 'X-Api-Key': 'YOUR_API_KEY' },
  body: form,
})
const { data } = await res.json()
// pass data.id in attachmentIds when creating a ticket or comment

JavaScript — OAuth application

javascript
const form = new FormData()
form.append('file', fileInput.files[0])

const res = await fetch('https://vibecode.bitrix24.com/v1/feedback/attachments', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
  },
  body: form,
})
const { data } = await res.json()

Response fields

Field Type Description
success boolean Always true on success
data object The uploaded attachment
data.id string Attachment UUID. Passed in attachmentIds when creating a ticket or comment
data.kind string Attachment class: IMAGE or DOCUMENT
data.mime string Type after processing. For an image — image/webp for PNG, JPEG, and WebP, image/gif for GIF. For a document — the type by extension, for example text/plain or application/zip
data.sizeBytes number Size of the stored file in bytes
data.width number Width in pixels. 0 for a document
data.height number Height in pixels. 0 for a document
data.thumbnailUrl string · null Path to the attachment thumbnail. null for a document
data.originalName string · null Original file name. null if the image was uploaded without a file name. A document without a name gets attachment.<extension>
data.expiresAt string The time after which an unlinked attachment is deleted (ISO 8601)

Response example

The image screenshot.png was uploaded:

JSON
{
  "success": true,
  "data": {
    "id": "15ae6203-e67c-4378-912c-9b0465360c79",
    "kind": "IMAGE",
    "mime": "image/webp",
    "sizeBytes": 560,
    "width": 640,
    "height": 360,
    "thumbnailUrl": "/v1/feedback/_orphan/15ae6203-e67c-4378-912c-9b0465360c79/thumb",
    "originalName": "screenshot.png",
    "expiresAt": "2026-10-09T09:27:47.506Z"
  }
}

The document note.txt was uploaded — no thumbnail and no dimensions:

JSON
{
  "success": true,
  "data": {
    "id": "f4ec1f18-431b-49ec-85bb-bedcfce6da7d",
    "kind": "DOCUMENT",
    "mime": "text/plain",
    "sizeBytes": 16,
    "width": 0,
    "height": 0,
    "thumbnailUrl": null,
    "originalName": "note.txt",
    "expiresAt": "2026-10-09T09:25:40.292Z"
  }
}

Error response example

413 — file larger than 10 MB:

JSON
{
  "success": false,
  "error": {
    "code": "IMAGE_TOO_LARGE",
    "message": "File too large: a feedback attachment is capped at 10 MB (10485760 bytes)",
    "hint": "Shrink the image below 10 MB (lower the resolution or re-encode as JPEG or WebP) and upload it again. The cap applies to each file and is not raised by retrying."
  }
}

Errors

HTTP Code Description
400 NO_FILE No file was passed in the file field
400 INVALID_MIME The file name extension is not on the allowed list. A file without an extension is rejected unless its Content-Type is image/png, image/jpeg, image/webp, or image/gif
400 MIME_MISMATCH The contents do not match the type: an image signature is not recognized as PNG, JPEG, WebP, or GIF, or a .zip or .xlsx file lacks the ZIP archive signature. A mismatch between two image formats is not an error — processing follows the actual contents
400 INVALID_FILE_EMPTY The file is empty
400 IMAGE_DECODE_FAILED The file could not be parsed as an image: the data is corrupted, the image has no dimensions, or processing did not finish within 5 seconds
400 IMAGE_TOO_MANY_PIXELS More than 24 million pixels
400 TOO_MANY_FRAMES The animation has more than 30 frames
402 ACCOUNT_FROZEN The balance is frozen — uploading attachments is unavailable while it is frozen
403 MANAGEMENT_KEY_READ_ONLY A management key cannot upload attachments
403 WRITE_BLOCKED_READONLY_KEY The key is read-only — see access rights
413 IMAGE_TOO_LARGE File larger than 10 MB. The same code applies to images and documents
401 MISSING_API_KEY The X-Api-Key header was not passed
429 RATE_LIMITED More than 5 uploads per minute from one key. This is the platform-wide cap and it is divided across the backend replicas, so the effective value arrives in the X-RateLimit-Limit header

For the full list of common API errors, see Errors.

Known specifics

An attachment lives for an hour before it is linked. An uploaded attachment is initially not linked to any ticket and is deleted after expiresAt (in one hour) if its id is not passed in attachmentIds when creating a ticket or comment. Once linked, the attachment is stored together with the ticket.

Format after processing. PNG, JPEG, and WebP are converted to WebP, so their mime in the response is image/webp. GIF is re-encoded as GIF (image/gif), preserving animation, up to 30 frames; beyond that the upload is rejected with 400 TOO_MANY_FRAMES. The sizeBytes, width, and height fields refer to the processed file. A document is stored byte for byte; its mime is determined by the extension, not the contents. An archive is never unpacked — for .zip and .xlsx only the ZIP signature is checked.

The attachment class is chosen by the file name extension, not by Content-Type. An image is processed by its actual contents, so a JPEG saved as image.png is accepted, and the mime in the response is the result of re-encoding. A file without an extension — for example blob, the default name FormData.append assigns to a Blob object — is accepted only as an image and only when its Content-Type is image/png, image/jpeg, image/webp, or image/gif.

Operations on a linked attachment

Once linked to a ticket, an attachment is available for download and deletion by its id (the data.attachments[].id field in Get a ticket). These operations work with binary data, so they have no JSON body and no standard set of 4 examples.

Download an attachment

GET /v1/feedback/:id/attachments/:attId/file

Returns the attachment file. Available to a key that has access to the ticket. An image arrives with its own type — image/webp or image/gif — and supports ETag and If-None-Match: a conditional request returns 304 Not Modified. A document always arrives as application/octet-stream with the Content-Disposition: attachment header and the original file name, without ETag.

Parameter Type Required Description
id (path) string yes Ticket UUID
attId (path) string yes Attachment UUID from data.attachments[].id
Terminal
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://vibecode.bitrix24.com/v1/feedback/a1b2c3d4-1111-2222-3333-444455556666/attachments/1209c405-1f99-4240-95e5-d5cc0cd43560/file" \
  -o attachment.webp

If the attachment does not exist, is not accessible to the key, or does not belong to the specified ticket, the request returns 404 NOT_FOUND. If the balance is frozen, it returns 402 ACCOUNT_FROZEN.

Attachment thumbnail

GET /v1/feedback/:id/attachments/:attId/thumb

Returns a scaled-down thumbnail of an image. Content-Type is always image/jpeg. Parameters and access rules are the same as for the file download. A document has no thumbnail — the request returns 404 NOT_FOUND.

Terminal
curl -H "X-Api-Key: YOUR_API_KEY" \
  "https://vibecode.bitrix24.com/v1/feedback/a1b2c3d4-1111-2222-3333-444455556666/attachments/1209c405-1f99-4240-95e5-d5cc0cd43560/thumb" \
  -o thumb.jpg

Delete an attachment

DELETE /v1/feedback/:id/attachments/:attId

Deletes the attachment. Available to whoever uploaded it. On success, returns 204 No Content with an empty body. Deletion is not allowed if the other side has already replied after the attachment was uploaded.

Parameter Type Required Description
id (path) string yes Ticket UUID
attId (path) string yes Attachment UUID
reason (body) string no Deletion reason, 3–500 characters
Terminal
curl -X DELETE -H "X-Api-Key: YOUR_API_KEY" \
  "https://vibecode.bitrix24.com/v1/feedback/a1b2c3d4-1111-2222-3333-444455556666/attachments/1209c405-1f99-4240-95e5-d5cc0cd43560"
HTTP Code Description
402 ACCOUNT_FROZEN The balance is frozen
403 DELETE_NOT_ALLOWED The other side has already replied after the attachment was uploaded
403 WRITE_BLOCKED_READONLY_KEY The key is read-only — see access rights
400 REASON_INVALID reason shorter than 3 or longer than 500 characters
404 NOT_FOUND The attachment was not found, is not owned by the key, or does not belong to the ticket

See also