Skip to content

Files & Attachments

Send files with a message: upload them once through IFiles and name them by fileId, or carry them as parts of the IMessages/Send request itself.

Overview #

A message carries up to 10 attachments of any type, each up to 10 MiB. The bot needs AttachFiles in the channel on top of SendMessages. There are two ways to hand the files over:

Way When
Upload, then send by fileId The message stays a plain JSON request. An upload can go into any number of messages for 24 hours, and a set of files larger than one request holds goes up a file at a time.
One multipart Send One request for the message and its files, up to about 10 MiB of parts in total.

Only IMessages/Send takes attachments; IInteractions/Reply does not. Sticker and emoji files go through the same IFiles/Upload with another purpose — see Stickers & Custom Emoji → Files.

Uploading with IFiles #

POST /IFiles/v1/Upload is a multipart/form-data request with a file part and purpose=attachment. Any type is taken, up to 10 MiB. The part's file name and Content-Type are kept as the attachment's fileName and contentType (application/octet-stream when the part declares none).

Upload

curl -X POST \
     -H "Authorization: Bot YOUR_TOKEN" \
     -F "purpose=attachment" \
     -F "[email protected];type=application/pdf" \
     https://gateway.argon.zone/IFiles/v1/Upload

Response (BotFileV1)

{
  "fileId": "5f1d0c3a-...",
  "size": 482113,
  "contentType": "application/pdf",
  "url": ".../files/5f1d0c3a-...",
  "fileName": "report.pdf"
}

An upload lives 24 hours. Within them its fileId can go into any number of messages; a sent message keeps its files after that. A bot holds at most 100 unused uploads at a time — the next returns 422 quota_exceeded — and an upload stops counting once a message carries it. GET /IFiles/v1/Get?fileId=… describes an upload again.

Sending by fileId #

Name the uploads in the attachments list of an ordinary JSON IMessages/v1/Send. Only uploads of this bot with purpose attachment are taken; a sticker or emoji upload is 404 not_found here.

Request

curl -X POST \
     -H "Authorization: Bot YOUR_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"channelId":"c0ffee00-...","text":"Weekly report","randomId":7311,"attachments":["5f1d0c3a-..."]}' \
     https://gateway.argon.zone/IMessages/v1/Send

Response

{
  "messageId": 42,
  "attachments": [
    {
      "fileId": "5f1d0c3a-...",
      "url": ".../files/5f1d0c3a-...",
      "fileName": "report.pdf",
      "size": 482113,
      "contentType": "application/pdf",
      "width": null,
      "height": null
    }
  ]
}

The response describes every attachment in the order given (BotAttachmentV1), so the bot has the file URLs without reading the message back.

Multipart Requests #

IMessages/v1/Send also takes multipart/form-data with the same fields. Every field is a text part; entities and controls are one field holding their JSON. The attachments field is repeated once per file, and each file can be given three ways:

You send Meaning
a file part named attachments The part is the file. Repeat it for more files.
attachments=attach://chart A text field naming another part of the same request (chart). A name that matches no part returns 400 invalid_request.
attachments=5f1d0c3a-... The fileId of an earlier upload, as in JSON.

Three files: a named part, an upload and a part of its own

curl -X POST \
     -H "Authorization: Bot YOUR_TOKEN" \
     -F "channelId=c0ffee00-..." \
     -F "text=Chart, report and raw data" \
     -F "randomId=7312" \
     -F "attachments=attach://chart" \
     -F "attachments=5f1d0c3a-..." \
     -F "[email protected];type=image/png" \
     -F "[email protected];type=text/csv" \
     https://gateway.argon.zone/IMessages/v1/Send

The text fields come first in the message, in their order, then the parts named attachments: the example sends chart.png, the uploaded report and data.csv. A part sent this way does not count against the 100 unused uploads.

One multipart request carries about 10 MiB of parts in total — one file at its largest — and more is refused with 413 too_large. Upload larger sets first and send them by fileId. An attach:// reference in a JSON body is 400 invalid_request: there are no parts to name.

Reading Attachments #

Messages carry their files as attachments — in GET /IMessages/v1/History, in the messageCreate event and in the Send response — whoever sent them. On a message without files it is null (an empty list in the Send response).

Field Type Description
fileId string The file's ID.
url string Where to download it.
fileName string The name it was sent with, without any directory; at most 255 characters.
size int64 In bytes.
contentType string The media type it was sent with.
width, height int32 | null Set for an image the server could read; null for anything else.

SSE example — event: messageCreate, data: (excerpt)

{
  "spaceId": "aaaabbbb-...",
  "channelId": "c0ffee00-...",
  "message": {
    "messageId": 44,
    "text": "This week's chart",
    "attachments": [
      {
        "fileId": "9e8d7c6b-...",
        "url": ".../files/9e8d7c6b-...",
        "fileName": "chart.png",
        "size": 48211,
        "contentType": "image/png",
        "width": 1200,
        "height": 800
      }
    ]
  }
}

The message's attachment entities describe the same files and carry the same url — see Message Entities. Read attachments; it is the simpler of the two.

Errors #

Besides the refusals every message can get (cannot_send, slow_mode, …), a message with files, or an upload, can be refused with these. Nothing is sent when one of them comes back.

Status Code Meaning
400 validation_error More than 10 attachments in one message.
400 invalid_format A file is empty.
400 invalid_request The body could not be read: a field is missing or malformed, an attach:// names no part, or an attach:// is in a JSON body.
403 insufficient_permissions The bot has no AttachFiles in this channel.
404 not_found A fileId names no attachment upload of this bot: it is unknown, over 24 hours old, or was uploaded for a sticker or emoji.
413 too_large A file is over 10 MiB, or the request over its limit.
422 quota_exceeded IFiles/Upload only: the bot holds 100 unused uploads. Send some, or wait for them to expire.

Branch on the error code, not the status — see Error Handling.

Limits & Permissions #

Limit Value Description
Attachments per message 10 More is validation_error.
File size 10 MiB Any type.
Request size ≈ 10 MiB A multipart Send or IFiles/Upload: 10 MiB of parts plus the form around them.
Uploads 100 · 24 h Unused uploads a bot may hold, and how long an upload can be sent.
Rate limit 30/min · 120/min IFiles · IMessages, sliding window per bot.
Required permission AttachFiles In the channel, for a message with attachments. Uploading needs none.

Next Steps