---
title: asset_create_upload
description: Create an Asset from bytes that the client holds. The client sends the file to a short-lived upload URL.
---

Ask to add a file to AdCrunch when the agent holds the bytes and the file has no public URL. AdCrunch creates an Asset at `pending` and gives a short-lived upload URL.

> "Add the image in this folder to AdCrunch."

The agent sends an HTTP `PUT` with the bytes to the upload URL, with the header `If-None-Match: *`. A `PUT` without that header answers `403`. The `PUT` needs no `Content-Type`, because AdCrunch reads the type from the bytes. The upload URL can create the file once, and it can never replace it. A `PUT` that answers `412` found the file already stored, so the agent continues. The agent then calls [`asset_get`](/mcp/tools/asset-get) until the Asset is `ready` or `failed`. A read of the Asset checks the file and finalizes it. A `ready` Asset shows in the Assets of your organization. The agent then calls [`asset_register`](/mcp/tools/asset-register).

The upload URL is on `*.r2.cloudflarestorage.com`. An agent that cannot reach that host uses [`asset_create`](/mcp/tools/asset-create) with the public URL of the file.

This tool is part of the job [Bring your own creative](/mcp/tools/bring-your-own-creative).

## Reference

Create an Asset from bytes that you hold, and get a short-lived URL to send them to.

**You upload the bytes yourself**: send an HTTP `PUT` to `upload_url` with the file as the body and the header `If-None-Match: *`. A `PUT` without that header answers `403`. No `Content-Type` is needed: AdCrunch reads the type from the bytes. The bytes must never pass through this conversation. Read the file and send the `PUT` from your own runtime.

The URL can create the object once, and never replace it. A `PUT` that answers `412` found the bytes already stored, for example after a retry: continue as after a `200`. To use a different file, create a new Asset.

Then poll asset_get until `status` is `ready` or `failed`. A read of the Asset checks the file and finalizes it. When `status` is `failed`, `failureCode` and `failureReason` say why. When `status` is `ready`, call asset_register to place the Asset in an advertiser library.

The upload URL is on `*.r2.cloudflarestorage.com`. A client that cannot reach that host cannot use this tool. It uses asset_create with the public URL of the file instead.

### Input

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `filename` | string | yes | The original filename, including its extension. |

### Output

A successful call returns this object in `structuredContent`.

| Field | Type | Always present | Description |
| --- | --- | --- | --- |
| `asset_id` | string | yes | The Asset that the call created, at `pending`. Poll asset_get with this id. |
| `expires_at` | number | yes | When the upload URL stops being accepted, in milliseconds since the Unix epoch, UTC. |
| `upload_url` | string | yes | Send the bytes here with an HTTP `PUT` and the header `If-None-Match: *`. The URL is the credential: anybody who holds it can create that one object until it expires. It can never replace the object. |

### 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.

- `reservation_failed`
- `organization_unentitled`
- `forbidden`
- `invalid_request`
- `internal_error`

### Scope

The token must hold `asset: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
{
  "filename": "spring-sale.mp4"
}
```

The result, in `structuredContent`:

```json
{
  "asset_id": "ast_7c1e9a",
  "expires_at": 1790003600000,
  "upload_url": "https://b7f3c81a29d64e0fa5c3d1e8b0947265.r2.cloudflarestorage.com/assets/org_3a7f10/ast_7c1e9a?X-Amz-Expires=3600&X-Amz-Date=20260921T141320Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=4f8e2a6c1b9d7e3f5a0c2b4d6e8f1a3c%2F20260921%2Fauto%2Fs3%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Signature=7d1c4a9f2e8b0536c1a7e94d2b6f038a5c1e9b7d3f6a2c805d4e1b93a7f0c625"
}
```
