---
title: "Complete a file upload"
locale: en
url: https://tlg.builera.app/en/docs/reference/rest/files.complete_upload
verified: 2026-10-24
app_version: "2.0"
generated: true
---

# Complete a file upload

Step 2 after the PUT: the server checks the real size, the type from the first bytes (an image/video whose bytes differ → file_type_mismatch) and SHA-256, then copies it to a fixed place. Returns `storagePath`, `kind`, `mime`, `size` — pass exactly these to messages.send_media / send_album, stories.post, accounts.update_photo, groups.set_photo, bots.set_photo or a campaigns.save_graph "send file" block. Ready files expire after 24 hours unused; message / story / photo files can be used once. Safe to call again.

Step 2 after the PUT: the server checks the real size, the type from the first bytes (an image/video whose bytes differ → file_type_mismatch) and SHA-256, then copies it to a fixed place. Returns `storagePath`, `kind`, `mime`, `size` — pass exactly these to messages.send_media / send_album, stories.post, accounts.update_photo, groups.set_photo, bots.set_photo or a campaigns.save_graph "send file" block. Ready files expire after 24 hours unused; message / story / photo files can be used once. Safe to call again.

## Overview

| Name | Value |
| --- | --- |
| Code | `files.complete_upload` |
| Scope needed | `messages.send` |
| Risk | Write |
| May need confirmation | No |
| Runs as | Answers right away |
| Needs `Idempotency-Key` | No |
| Member roles allowed | `owner`, `manager`, `agent` |
| In the app | Assistant (Build 2.1) |

## Input fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `uploadId` | `uuid` | Yes |   |

## Example request

```bash
curl -X POST https://tlg.builera.app/api/public/v2/ops/files.complete_upload \
  -H "Authorization: Bearer $TELEGRAM_AUTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"uploadId":"00000000-0000-0000-0000-000000000000"}'
```

## Output fields (`data`)

| Field | Type | Required |
| --- | --- | --- |
| `ok` | `true` | Yes |
| `uploadId` | `text` | Yes |
| `status` | `"expired" \| "pending" \| "checking" \| "ready" \| "rejected" \| "used"` | Yes |
| `purpose` | `"message" \| "campaign" \| "photo" \| "story"` | Yes |
| `accountId` | `null \| text` | Yes |
| `campaignId` | `null \| text` | Yes |
| `fileName` | `text` | Yes |
| `storagePath` | `null \| text` | Yes |
| `kind` | `null \| "video" \| "photo" \| "audio" \| "voice" \| "document"` | Yes |
| `mime` | `null \| text` | Yes |
| `size` | `null \| number` | Yes |
| `sha256` | `null \| text` | Yes |
| `code` | `null \| text` | Yes |
| `uploadExpiresAt` | `text` | Yes |
| `readyExpiresAt` | `null \| text` | Yes |
| `usedAt` | `null \| text` | Yes |

## Example answer

```json
{
  "data": {
    "ok": true,
    "uploadId": "00000000-0000-4000-8000-000000000000",
    "status": "expired",
    "purpose": "message",
    "accountId": "00000000-0000-4000-8000-000000000000",
    "campaignId": "00000000-0000-4000-8000-000000000000",
    "fileName": "text",
    "storagePath": "text",
    "kind": "video",
    "mime": "text",
    "size": 1,
    "sha256": "text",
    "code": "text",
    "uploadExpiresAt": "2026-10-22T12:00:00.000Z",
    "readyExpiresAt": "2026-10-22T12:00:00.000Z",
    "usedAt": "2026-10-22T12:00:00.000Z"
  }
}
```

## Using it over MCP

Not listed on any profile: call `tools_describe` then `tools_run` with `code: "files.complete_upload"`. Tool name when listed: `files_complete_upload`.
