> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runflow.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload large files

> Three-step presigned upload for files too large for a JSON body.

## What you'll do

Upload a video or large image directly to Runflow's storage and reference the resulting asset by ID in subsequent run requests.

## When to use this

`POST /v1/asset-uploads` accepts `size_bytes` up to 50 MiB (52,428,800 bytes); larger uploads are rejected at create time.

| Input size      | Approach                                                                                           |
| --------------- | -------------------------------------------------------------------------------------------------- |
| \< 1 MiB        | Inline base64 or a public URL is fine.                                                             |
| 1 MiB to 50 MiB | Inline if convenient, presigned upload if you control the server.                                  |
| > 50 MiB        | Not supported by the asset-upload API. Host the file at a public URL and pass that URL on the run. |

## Prerequisites

* A Runflow API key.
* The file on disk or in memory.

## Steps

<Steps>
  <Step title="Create an upload">
    Get a presigned URL. Tell Runflow the filename, MIME type, and exact byte size.

    ```bash theme={"dark"}
    curl -X POST https://api.runflow.io/v1/asset-uploads \
      -H "Authorization: Bearer $RUNFLOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "filename": "perfume-bottle.webp",
        "mime_type": "image/webp",
        "size_bytes": 2400000
      }'
    ```

    Response (201 Created):

    ```json theme={"dark"}
    {
      "asset_id": "01J0...",
      "upload_url": "https://storage.runflow.io/..."
    }
    ```
  </Step>

  <Step title="PUT the file to upload_url">
    Use the URL exactly as returned. No auth header, the presigned URL carries the credentials.

    ```bash theme={"dark"}
    curl -X PUT "$UPLOAD_URL" \
      -H "Content-Type: image/webp" \
      --upload-file ./perfume-bottle.webp
    ```
  </Step>

  <Step title="Confirm the upload">
    Tell Runflow the upload is complete. The body is optional; pass `folder_id` to file the asset under a folder.

    ```bash theme={"dark"}
    curl -X POST https://api.runflow.io/v1/asset-uploads/{asset_upload_id}/confirmations \
      -H "Authorization: Bearer $RUNFLOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{}'
    ```

    Response (201 Created) is the full Asset record. The fields you'll most often read:

    ```json theme={"dark"}
    {
      "id": "01J0...",
      "name": "perfume-bottle.webp",
      "mime_type": "image/webp",
      "size_bytes": 2400000,
      "url": "https://...",
      "thumbnail_url": "https://...",
      "asset_type": "image",
      "folder_id": null,
      "created_at": "2026-05-06T10:00:00Z"
    }
    ```
  </Step>

  <Step title="Use the asset in a run">
    Reference it by `id` (or by `url` if the model accepts a public URL):

    ```bash theme={"dark"}
    curl -X POST https://api.runflow.io/v1/models/runflow/background-replace/runs \
      -H "Authorization: Bearer $RUNFLOW_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "input": {
          "asset_id": "01J0...",
          "prompt": "white studio"
        },
        "callback_url": "https://your-server.com/webhook"
      }'
    ```
  </Step>
</Steps>

## Verify it worked

```bash theme={"dark"}
curl https://api.runflow.io/v1/assets/{id} \
  -H "Authorization: Bearer $RUNFLOW_API_KEY"
```

A `200` with the Asset payload confirms it is usable.

## Troubleshooting

| Symptom               | Likely cause               | Fix                                                                                                                                          |
| --------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `403` on the PUT      | Presigned URL expired      | Re-create with `POST /v1/asset-uploads`.                                                                                                     |
| `422` on create       | Size or MIME type rejected | `size_bytes` max is 50 MiB; check `mime_type` against the supported list at [`GET /v1/assets/types`](/api-reference/media/list-asset-types). |
| Asset never confirmed | Confirmation step skipped  | POST to the confirmations endpoint.                                                                                                          |

## Related

<CardGroup cols={2}>
  <Card title="Assets API" icon="image" href="/api-reference/media/search-assets">List, get, delete assets.</Card>
  <Card title="Asset folders" icon="folder" href="/api-reference/media/search-asset-folders">Organize uploads.</Card>
</CardGroup>
