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

# Replace Asset File

> Swap the file body of an existing internal asset in place, keeping the same asset ID and links.

<Note>
  **Beta.** The direct upload flow is in active development. The request and
  response shapes shown here are stable for production use, but we may add new
  fields or capabilities without notice.
</Note>

Use this to ship a new version of a file without re-wiring anything. The asset
keeps its ID and stays attached to every digital product and order it was
already linked to, so buyers downloading it receive the new file
automatically — there is no need to create a fresh asset and re-attach it.

Only `internal` assets can be replaced. The flow mirrors
[creating an internal asset](./create-asset): the response includes a single
presigned `PUT` URL — upload the new file body to that URL to finish. Once the
replacement is committed, the previous file is removed and any watermarking is
re-applied to the new body.

Plan storage quota is enforced on the **growth** in size (new `byte_size` minus
the current size); the per-file ceiling is 2 GB.

## Path parameters

<ParamField path="id" type="string" required>
  The ID (UUID) of the internal asset to replace.
</ParamField>

## Request body

<ParamField body="asset" type="object" required>
  <Expandable title="Asset fields">
    <ParamField body="filename" type="string" required>
      Suggested attachment name on download for the new file.
    </ParamField>

    <ParamField body="byte_size" type="integer" required>
      Size of the new file in bytes. Must be positive and at most 2 GB.
    </ParamField>

    <ParamField body="content_type" type="string">
      Optional MIME type for the new file. Defaults to the asset's current
      content type, or `application/octet-stream`.
    </ParamField>
  </Expandable>
</ParamField>

## Response

<ResponseField name="asset" type="object" required>
  The asset, with its ID unchanged.
</ResponseField>

<ResponseField name="upload" type="object" required>
  A single-PUT handle (`method: "PUT"`) with the presigned URL and headers
  required to upload the new file body. See [Upload Asset](./upload-asset) for
  the field-by-field breakdown.
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST 'https://app.filemonk.io/api/v1/external/assets/a1b2c3d4-e5f6-7890-abcd-ef1234567890/replace' \
    -H 'X-Auth-Token: fm_api_key_your_key_here' \
    -H 'Content-Type: application/json' \
    -d '{
      "asset": {
        "filename": "brushes-pack-v3.zip",
        "byte_size": 2097152,
        "content_type": "application/zip"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  import { readFile, stat } from 'node:fs/promises';

  const ASSET_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
  const path = './brushes-pack-v3.zip';
  const { size } = await stat(path);

  const replace = await fetch(
    `https://app.filemonk.io/api/v1/external/assets/${ASSET_ID}/replace`,
    {
      method: 'POST',
      headers: {
        'X-Auth-Token': 'fm_api_key_your_key_here',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        asset: {
          filename: 'brushes-pack-v3.zip',
          byte_size: size,
          content_type: 'application/zip'
        }
      })
    }
  ).then(r => r.json());

  // Upload the new body to the presigned URL.
  await fetch(replace.upload.url, {
    method: 'PUT',
    headers: replace.upload.headers,
    body: await readFile(path)
  });
  ```

  ```python Python theme={null}
  import os
  import requests

  ASSET_ID = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
  path = './brushes-pack-v3.zip'

  replace = requests.post(
      f'https://app.filemonk.io/api/v1/external/assets/{ASSET_ID}/replace',
      headers={'X-Auth-Token': 'fm_api_key_your_key_here'},
      json={
          'asset': {
              'filename': 'brushes-pack-v3.zip',
              'byte_size': os.path.getsize(path),
              'content_type': 'application/zip'
          }
      }
  ).json()

  with open(path, 'rb') as f:
      requests.put(
          replace['upload']['url'],
          headers=replace['upload']['headers'],
          data=f
      )
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "asset": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "type": "internal",
      "name": "Photoshop Brushes Pack",
      "filename": "brushes-pack-v3.zip",
      "content_type": "application/zip",
      "byte_size": 2097152,
      "watermark_asset": false,
      "autogenerate_licence_key": false,
      "created_at": "2026-04-08T10:00:00Z",
      "updated_at": "2026-04-09T12:30:00Z"
    },
    "upload": {
      "method": "PUT",
      "url": "https://<bucket>.r2.cloudflarestorage.com/external_api/<shop_id>/9c4b.zip?X-Amz-Signature=...",
      "headers": {
        "Content-Type": "application/zip",
        "Content-Disposition": "inline; filename=\"brushes-pack-v3.zip\""
      },
      "expires_at": "2026-04-09T13:30:00Z"
    }
  }
  ```

  ```json 422 theme={null}
  {
    "error": "Only internal assets can have their file replaced"
  }
  ```

  ```json 404 theme={null}
  {
    "error": "Asset not found"
  }
  ```
</ResponseExample>
