---
title: document_finalize
description: Complete a document upload once the bytes have landed — size and type are read from the stored file, not from what you declared.
---

Completes a document upload after you've `PUT` the bytes to the URL from [`document_create_upload`](/mcp/tools/document-create-upload). Until you call this, the file is invisible — reserved, but not attached to anything.

## Input

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `document_id` | `string` | yes | The id returned when you reserved the upload. |

## Output

The finished document: `{ id, name, filename, mimeType, sizeBytes, url }`.

## What actually gets checked

Size and content type are read from **the stored file**, never from what you declared when reserving. An upload URL carries no policy — nothing could have stopped you sending something else, or more of it — so this is the only point where what landed is really known.

That means finalize can reject a file the upload itself accepted:

- **wrong type** — what arrived isn't a PDF or an accepted image, whatever you declared when reserving. The file is discarded, not kept.
- **too large** — over 25 MB. Also discarded.
- **nothing there** — the bytes haven't arrived yet. Finish the `PUT` and retry.

:::tip[Safe to retry]

Finalizing an already-finalized document just returns it again. If you're unsure whether a call landed, call it again rather than re-uploading.

:::

Requires the `brand:write` scope.

## Reference

Complete a document upload once you have PUT the bytes to the URL from document_create_upload. Size and type are read from what actually landed, not from what you declared — so this can reject a file that the upload itself accepted.

### Input

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `document_id` | string | yes | The document id returned by document_create_upload. |

### Failure codes

A failed call has `isError` set, and `structuredContent.error` holds one of these codes. [Errors](/mcp/errors) describes the shape of a failed call.

- `not_found`
- `no_object`
- `unsupported_type`
- `too_large`
- `forbidden`
- `invalid_request`
- `internal_error`

### Scope

The token must hold `brand:write`. [Auth & scopes](/mcp/auth) lists each scope.

### Annotations

A client reads these hints. A hint that the tool does not declare has the default value of the MCP specification.

- **Writes.** The tool can change data.
- **Destructive.** The tool can make a change that you cannot undo. A client can ask you to confirm before it calls the tool.
- **Idempotent.** A second call with the same arguments changes nothing more.
- **Closed world.** The tool reads and writes the data of AdCrunch only.
