---
title: asset_create
description: Create an Asset from the public URL of an image or a video. AdCrunch fetches and checks the file in the background.
---

Ask to add a file to AdCrunch from its URL. AdCrunch creates an Asset at `pending`, fetches the file from the URL, and checks its type and its size.

> "Add the image at https://example.com/spring-sale.jpg to AdCrunch."

The agent calls this tool with a public `https` URL. AdCrunch does not import from an IP address, a local host, or an AdCrunch domain such as `adcrunch.dev`: for a file there, the agent uses [`asset_create_upload`](/mcp/tools/asset-create-upload). The call returns at once. The agent then calls [`asset_get`](/mcp/tools/asset-get) until the Asset is `ready`. When the file fails the check, the Asset is `failed`, and `failureCode` and `failureReason` say why. A `ready` Asset shows in the Assets of your organization. The agent then calls [`asset_register`](/mcp/tools/asset-register).

An agent that has a local file and no URL cannot use this tool. It uses the REST API with an API key.

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

## Reference

Create an Asset from the URL of an image or a video.

AdCrunch fetches the file from `source_url` in the background, stores it, and checks its type and size. The URL must use `https` and must be public. AdCrunch does not import from an IP address, a local host, or an AdCrunch domain. The tool returns at once with the Asset at `pending`.

Poll asset_get until `status` is `ready`, then call asset_register to place the Asset in an advertiser library. When `status` is `failed`, `failureCode` and `failureReason` say why. Create a new Asset after a failure.

### Input

| Argument | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no | A name to show in place of the filename. |
| `source_url` | string | yes | The public `https` URL of the image or video. |

### Output

A successful call returns this object in `structuredContent`.

| Field | Type | Always present | Description |
| --- | --- | --- | --- |
| `asset` | object | yes | The Asset, at `pending`. Poll asset_get until `status` is `ready` or `failed`. |
| `asset.contentHash` | string | yes | AdCrunch’s own hash of the stored bytes. A hint for de-duplication, never an identity, and never a provider’s hash. Empty when the store reports none. |
| `asset.createdAt` | number | yes | Milliseconds since the Unix epoch, UTC. |
| `asset.createdBy` | string | yes | The user who reserved the upload. |
| `asset.deletedAt` | number or null | yes | Always `null` here. A deleted Asset is not answered. |
| `asset.durationMs` | number or null | yes | How long the video runs, in milliseconds, read from the header of the file at finalize. `null` for an image. For a video, `null` when the file does not state it, or for an Asset uploaded before AdCrunch read it. |
| `asset.failureCode` | string or null | yes | Why AdCrunch refused the file, as a code. Branch on this field, never on `failureReason`. `unsupported_type`: the bytes are not an accepted image or video. `too_large`: the file is over the size limit. `unreadable`: AdCrunch could not read the header of the file. `source_unreachable`: an import could not fetch the file from its source URL. `source_length_unknown`: the source of an import sent no `Content-Length`. `null` unless `status` is `failed`. |
| `asset.failureReason` | string or null | yes | Why AdCrunch refused the file, as a sentence. `null` unless `status` is `failed`. |
| `asset.filename` | string | yes | The name the file was uploaded under. Never a lookup key. |
| `asset.height` | number or null | yes | The height in pixels as the file is displayed, with its rotation applied, read from the header of the file at finalize. `null` when the file does not state it, or for an Asset uploaded before AdCrunch read it. |
| `asset.id` | string | yes | The id of the Asset, `ast_…`. |
| `asset.key` | string | yes | Where the bytes are held. Read-only, and never sent by you. |
| `asset.kind` | one of `image`, `video` | yes | Follows from `mimeType`. It selects how a provider takes the file. |
| `asset.mimeType` | string | yes | The type that finalize identified from the stored bytes. The `Content-Type` of the upload does not set it. |
| `asset.name` | string or null | yes | The name a person gave this Asset, and what a provider receives at a future registration. `null` until somebody renames it, and the filename is the fallback. |
| `asset.organizationId` | string | yes | The organization that owns the Asset. |
| `asset.sizeBytes` | number | yes | Read from the stored object at finalize. |
| `asset.status` | one of `pending`, `ready`, `failed` | yes | `ready` once AdCrunch has inspected the bytes. Only a `ready` Asset can be registered. `pending`: the bytes have not been inspected yet. `failed`: AdCrunch refused the file and deleted it. `failureCode` and `failureReason` say why. A read answers an Asset in every status. A list answers `ready` Assets only. |
| `asset.updatedAt` | number or null | yes | When the Asset last changed, in milliseconds since the Unix epoch, UTC: at finalize, or at the last rename. |
| `asset.width` | number or null | yes | The width in pixels as the file is displayed, with its rotation applied, read from the header of the file at finalize. `null` when the file does not state it, or for an Asset uploaded before AdCrunch read it. |

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

- `invalid_url`
- `not_found`
- `source_not_allowed`
- `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.
- **Open world.** The tool reaches a system outside AdCrunch, such as an ad platform.

### Example

The arguments:

```json
{
  "name": "Spring sale 9:16",
  "source_url": "https://cdn.example.com/spring-sale.mp4"
}
```

The result, in `structuredContent`:

```json
{
  "asset": {
    "contentHash": "",
    "createdAt": 1790000000000,
    "createdBy": "usr_2e5b84",
    "deletedAt": null,
    "durationMs": null,
    "failureCode": null,
    "failureReason": null,
    "filename": "spring-sale.mp4",
    "height": null,
    "id": "ast_7c1e9a",
    "key": "org_3a7f10/ast_7c1e9a",
    "kind": "image",
    "mimeType": "",
    "name": "Spring sale 9:16",
    "organizationId": "org_3a7f10",
    "sizeBytes": 0,
    "status": "pending",
    "updatedAt": 1790000120000,
    "width": null
  }
}
```
