---
title: document_create_upload
description: Reserve a file on a brand, and get a URL for its bytes. This is the first step of an upload.
---

Ask to add a file to a brand. This tool is the first of three steps: it reserves the file and gives the agent a URL for the bytes.

> "Upload our brand book to Acme."

The agent calls `document_create_upload`. Then it sends the file to that URL from its own runtime, and it finishes with [`document_finalize`](/mcp/tools/document-finalize). You then see the brand book in the files of Acme.

The job guide [Describe a brand](/mcp/tools/describe-a-brand) shows each step of the job.

## Reference

Reserve a document against a brand. The tool returns a short-lived upload URL on `*.r2.cloudflarestorage.com`. Uploading needs a client that can reach that host. If yours cannot, tell the user that you cannot complete the upload from here. Upload the file yourself with an HTTP PUT to that URL, then call document_finalize. The bytes never pass through this tool — only your runtime touches them.

### Input

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `brand_name` | string | yes | The brand slug this document belongs to. |
| `content_type` | string | yes | The file's content type. Accepted: application/pdf, image/gif, image/jpeg, image/png, image/webp. |
| `filename` | string | yes | The original filename, for display. |

### Output

A successful call returns this object in `structuredContent`.

| Field | Type | Always present | Description |
| --- | --- | --- | --- |
| `documentId` | string | yes | The id of the reserved document (`doc_…`). Send it to `document_finalize`. |
| `expiresInSeconds` | number | yes | How long `uploadUrl` stays valid, in seconds. When it expires, reserve again. |
| `uploadUrl` | string | yes | A presigned URL. Send the bytes of the file to it with an HTTP `PUT`. Each person who has the URL can write the file until the URL expires. |

### 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`
- `unsupported_type`
- `organization_unentitled`
- `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.
- **Not destructive.** The tool makes only additive changes.
- **Not idempotent.** A second call with the same arguments can change more.
- **Closed world.** The tool reads and writes the data of AdCrunch only.

### Example

The arguments:

```json
{
  "brand_name": "acme",
  "content_type": "application/pdf",
  "filename": "acme-brand-guidelines.pdf"
}
```

The result, in `structuredContent`:

```json
{
  "documentId": "doc_5e2b41",
  "expiresInSeconds": 900,
  "uploadUrl": "https://3f9c2a7b1e0d4c6a8b5e2f1d9c7a4b60.r2.cloudflarestorage.com/documents/org_3a7f10/doc_5e2b41?X-Amz-Expires=900&X-Amz-Date=20260925T184612Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=5b2e7c1f9a0d4e3b8c6f2a1d7e9b4c05%2F20260925%2Fauto%2Fs3%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=e2ea52baf6fb964e4a650eb7aa0341ae656d1cdf36e8923a247576ec9273f2ca"
}
```
