Skip to content
donutchat.com

Attachments

Events tell your bot which files people attached to a message or thread reply, and the bot API lets you download each file. A bot can also attach files to its own thread replies when its owner allows it: see Attach files. Bots can’t attach files to top-level messages.

message.new and thread.reply.new events always include an attachments array, which is [] when there are no files. Events don’t contain file URLs.

{
"event_id": "evt_1793957400_3f9a1c2b7d4e",
"type": "message.new",
"timestamp": "2026-11-06T09:30:00Z",
"data": {
"message_id": 5678,
"chat_id": 1234,
"chat_name": "Release team",
"chat_type": "group",
"sender": { "id": 42, "kind": "user", "name": "Alice Kim", "username": "alice" },
"text": "@releasebot what does this error mean?",
"content_type": "TX",
"message_type": "image",
"attachments": [
{
"id": 2468,
"file_type": "image",
"mime_type": "image/png",
"original_filename": "build-error.png",
"file_size": 284113,
"width": 1170,
"height": 2532
}
],
"mentions_bot": true,
"thread_reply_count": 0
}
}
FieldTypeDescription
idintegerThe attachment’s ID. Use it in the download URL.
file_typestringimage, video, audio, document or other.
mime_typestringThe file’s MIME type.
original_filenamestringThe file’s name when it was shared.
file_sizeintegerSize in bytes.
width, heightintegerDimensions in pixels.
duration_msintegerLength in milliseconds.

Only id and file_type are always present. The other fields are left out when they’re empty or zero.

For a file on a top-level message, use chat_id, message_id and the attachment’s id from the message.new event:

GET https://api.donutchat.com/bots/v1/chats/{chat_id}/messages/{message_id}/attachments/{attachment_id}/content

For a file on a thread reply, use chat_id, parent_message_id, reply_id and the attachment’s id from the thread.reply.new event:

GET https://api.donutchat.com/bots/v1/chats/{chat_id}/messages/{parent_message_id}/replies/{reply_id}/attachments/{attachment_id}/content

The parent and linked_messages objects in an event carry their own attachments. Download those with the same two endpoints, using the object’s message_id (and its reply_id when it has one) instead of the event’s.

A download works when:

  • The request has your bot token in an Authorization: Bearer header.
  • The bot is an active member of the chat.
  • The message or reply still exists and belongs to that chat (and, for a reply, to that parent message).
  • The attachment belongs to that message or reply and is still available.
  • The file is at most 10 MiB (10,485,760 bytes).
Terminal window
curl -fsS -o build-error.png \
-H "Authorization: Bearer donutbot_YOUR_TOKEN_HERE" \
https://api.donutchat.com/bots/v1/chats/1234/messages/5678/attachments/2468/content

A successful download returns 200 with the file’s bytes as the body and these headers:

HeaderValue
Content-TypeThe file’s MIME type
Content-LengthThe file’s size in bytes
Content-Dispositionattachment
Cache-Controlprivate, no-store
X-Content-Type-Optionsnosniff

Treat original_filename as text from a chat member: don’t use it as a file path without cleaning it up.

Errors return JSON in the form {"ok": false, "error": "<code>", "message": "<text>"}.

HTTPerrorCause
400invalid_idAn ID in the path isn’t a positive 64-bit integer.
401unauthorizedThe token is missing or invalid, or the bot is paused.
403attachment_unavailableThe bot isn’t an active member of the chat.
404attachment_unavailableThe message, reply or attachment doesn’t exist, was deleted or has expired, or the IDs don’t belong together. For files on top-level messages, a bot that isn’t in the chat also gets 404.
413attachment_unavailableThe file is larger than 10 MiB.
429busyToo many downloads are in progress. Wait a moment and try again.
503unavailableDownloads are temporarily unavailable. Try again later.

A request with a method other than GET gets 405 with an empty body.