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

# Copy, move or rename a file

> Copy, move or rename a file inside Blob Storage with POST /v1/objects/copy, server-side and without downloading it. With move: true the source is removed.

<ParamField header="Authorization" type="string" placeholder="API Key" required>
  The API key for your account. You can find this in your [account settings](https://squarecloud.app/en/account/security).
</ParamField>

Object Copy duplicates a file under a new name, prefix or visibility, entirely on the server: no bytes go through your application. With `move: true` the source is removed afterwards, which is how you **rename** or **move** a file. Requires the `blob:write` scope and an active plan.

* The copy keeps the source's extension, content type, cache, disposition and metadata.
* A copy counts against your storage quota; a move doesn't.
* The destination always uses the current storage, so moving is also how you bring a legacy file, stored before the September 2026 update, to the new format (see [Object Update](/en/blob-reference/endpoint/update#legacy-files)).

## Parameters

<ParamField body="source" type="string" required>
  The id of the file to copy.
</ParamField>

<ParamField body="destination" type="object" required>
  Where the copy goes.

  <Expandable title="properties">
    <ParamField body="name" type="string" required>
      The new name, without extension. Same pattern as in [Object Post](/en/blob-reference/endpoint/post).
    </ParamField>

    <ParamField body="prefix" type="string">
      The new prefix. Omit it to store the copy at the root.
    </ParamField>

    <ParamField body="private" type="boolean">
      The visibility of the copy. Defaults to the source's.
    </ParamField>

    <ParamField body="expire" type="string | null">
      A new expiry counted from now (`30d`, `6h`). `null` removes it. Omit it to keep the source's expiry date.
    </ParamField>

    <ParamField body="security_hash" type="boolean" default="false">
      Adds a random suffix to the new name. Private copies always get it.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="move" type="boolean" default="false">
  `true` removes the source after the copy succeeds.
</ParamField>

<ParamField body="overwrite" type="boolean" default="false">
  `true` replaces an existing file at the destination. Otherwise the request answers `409 OBJECT_ALREADY_EXISTS`.
</ParamField>

## Rate limits

<Note>10 requests per 10 seconds (`RATE_LIMITED`, 429).</Note>

## Response

<ResponseField name="status" type="string">
  "success" if successful, "error" if not.
</ResponseField>

<ResponseField name="response" type="object">
  <Expandable title="Toggle object">
    <ResponseField name="id" type="string">
      The id of the new file.
    </ResponseField>

    <ResponseField name="private" type="boolean">
      Whether the new file is private.
    </ResponseField>

    <ResponseField name="url" type="string | null">
      The public URL of the new file, or `null` when private.
    </ResponseField>

    <ResponseField name="expires_at" type="ISO 8601">
      When the new file will be deleted. Present only when it expires.
    </ResponseField>

    <ResponseField name="size" type="number">
      The size of the file, in bytes.
    </ResponseField>

    <ResponseField name="source" type="string">
      The id of the source.
    </ResponseField>

    <ResponseField name="moved" type="boolean">
      Whether the source was removed.
    </ResponseField>

    <ResponseField name="replaced" type="boolean">
      Whether an existing file at the destination was replaced.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash Rename theme={"system"}
  curl --request POST \
    --url 'https://blob.squarecloud.app/v1/objects/copy' \
    --header 'Authorization: YOUR_API_KEY' \
    --header 'Content-Type: application/json' \
    --data '{
      "source": "pub/3155597145698959364/uploads/IMG_2041.png",
      "destination": { "name": "cover", "prefix": "posts/launch" },
      "move": true
    }'
  ```

  ```javascript JavaScript theme={"system"}
  const response = await fetch('https://blob.squarecloud.app/v1/objects/copy', {
    method: 'POST',
    headers: {
      Authorization: 'YOUR_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      source: 'pub/3155597145698959364/uploads/IMG_2041.png',
      destination: { name: 'cover', prefix: 'posts/launch' },
      move: true,
    }),
  });
  ```
</RequestExample>

<ResponseExample>
  ```json theme={"system"}
  {
    "status": "success",
    "response": {
      "id": "pub/3155597145698959364/posts/launch/cover.png",
      "private": false,
      "url": "https://blob.squarecloud.dev/pub/3155597145698959364/posts/launch/cover.png",
      "size": 416230,
      "source": "pub/3155597145698959364/uploads/IMG_2041.png",
      "moved": true,
      "replaced": false
    }
  }
  ```
</ResponseExample>

## Errors

| Code | HTTP | When |
| - | - | - |
| `INVALID_OBJECT` | 400 | `source` is missing, malformed or not yours. |
| `INVALID_OBJECT_NAME` / `INVALID_OBJECT_PREFIX` | 400 | The destination name or prefix doesn't match the pattern. |
| `INVALID_DESTINATION` | 400 | `private` or `security_hash` is not a boolean. |
| `INVALID_OBJECT_EXPIRE` | 400 | `expire` is not a duration from 1 hour to 1825 days. |
| `INVALID_OBJECT_SECURITY_HASH` | 400 | `security_hash: false` on a private copy. |
| `SAME_OBJECT` | 400 | The destination is the source. |
| `PERMISSION_DENIED` | 401 | The account has no active plan. |
| `UPGRADE_REQUIRED` | 403 | The expiry needs a higher plan. |
| `STORAGE_QUOTA_EXCEEDED` | 403 | The copy would pass the included storage. |
| `OBJECT_NOT_FOUND` | 404 | The source doesn't exist. |
| `OBJECT_ALREADY_EXISTS` | 409 | A file exists at the destination and `overwrite` is not `true`. |
| `RATE_LIMITED` | 429 | More than 10 requests in 10 seconds. |
| `COPY_FAILED` | 500 | The copy failed. Retry. |
| `PRIVATE_STORAGE_UNAVAILABLE` / `PUBLIC_STORAGE_UNAVAILABLE` | 503 | Storage is temporarily unavailable. Retry. |

## Related

* Blob SDK: [`blob.copy()`](/en/sdks/blob/objects), [`blob.move()`](/en/sdks/blob/objects)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.