---
title: "Send a file through an assistant"
locale: en
url: https://tlg.builera.app/en/docs/agent-playbooks/send-file
verified: 2026-10-25
app_version: "2.1"
---

# Send a file through an assistant

Hand Telegram-Auto a photo, video or document through a short-lived upload or a public HTTPS link, let it check the file, then send it.


## Goal

You put a file into Telegram-Auto so an account can send it, post it as a story, use it as a profile photo or use it in a campaign's "send file" step. The app checks the file itself (real size, file type from its first bytes, SHA-256) before it can be used.

## Permissions needed

- `messages.send` for all four file operations and for sending.
- The account that uses the file must be within the connection's scope.
- For a campaign: permission to edit that campaign.

## Ask the person before

Ask which file to send, to whom and from which account. Do not upload a file the person has not given you, and do not take files from someone else's computer or private folders.

## Check first

Call `whoami`. Pick the `purpose` that matches the job:

| `purpose` | Used by | Maximum |
|---|---|---|
| `message` | `messages.send_media`, `messages.send_album` | 50 MB |
| `story` | `stories.post` | 50 MB |
| `photo` | account / group / bot profile photos (`*.set_photo`) | 10 MB |
| `campaign` | a campaign's "send file" step (needs `campaignId`, no `accountId`) | 50 MB |

Each connection holds at most 20 files waiting to be uploaded and 50 ready files not yet used.

## Steps

There are two ways in. Both end in the same check.

**Option A — upload directly** (you have the file's bytes):

1. Call `files.create_upload` with `purpose`, `accountId` (or `campaignId`), `fileName`, `mime` and `size`.
2. `PUT` the raw bytes to `uploadUrl`, with exactly the `headers` from the answer, before `completeBefore` (2 hours).
3. Call `files.complete_upload` with `uploadId`. Calling it again is safe if the network is flaky.

**Option B — public link** (the file is already online):

1. Call `files.import_url` with a public HTTPS `url` (port 443, no user name or password in the link, no redirects, downloaded within 20 seconds). ChatGPT apps can pass their file parameter directly as `file: { download_url, file_name, mime_type }`.

Then:

4. When the status is `ready`, take the `storagePath`. You can read it again with `files.status`.
5. Put `storagePath` into the operation that uses it, for example `messages.send_media` (with an `Idempotency-Key`).
6. Follow the send with `operations_get`.

> [!IMPORTANT]
> Stop point — ask the person
>
> Sending a file sends a real message to a real person. Before calling the send operation, repeat the file, the recipient and the sending account so the person can confirm.

## Check after

A file is used **once**: after sending, its status becomes `used` and the same `storagePath` cannot be sent again. To send it again, upload it again. A ready file nobody uses is deleted after 24 hours.

## If something goes wrong

| Code | Meaning | What to do |
|---|---|---|
| `upload_missing` | Completed before the bytes were `PUT` | `PUT` again, then complete |
| `file_mismatch`, `file_type_mismatch` | The real bytes are a different type than declared | Declare the right type, or use another file |
| `file_type_not_allowed` | That file type does not fit the `purpose` (for example SVG as a profile photo) | Pick another file |
| `file_too_large` | Over the `purpose` limit | Compress it or pick a smaller file |
| `file_not_found` | The `storagePath` is not this connection's file | Only use paths the app returned |
| `file_not_ready` | The file was already used, expired or is still being checked | Upload it again |
| `url_blocked`, `url_redirect` | The link points into a private network or redirects | Use a direct public link |

Do not rename a file's extension to get past the check.

## Stop conditions

Stop when the file has been sent, when the file was refused and the person has not given another one, or when the person has not confirmed the send.

