---
title: CopyObject · S3 API
description: Copies an object to another key.
doc_version: 0.1.0-preview
last_updated: 2026-09-14
---

# CopyObject

`PUT /{bucket}/{key}` with the `x-amz-copy-source` header

Copies an object to another key.

Storage copies the bytes on its side, with no download. The source can be in another bucket of the same workspace. The copy keeps the metadata and tags of the source unless you replace them.

- Role: Editor or higher.
- Support: partial.
- Auth: AWS Signature Version 4 with your API key. See [S3 API](/api/s3.md).
- The source needs the Viewer role and must be in the same workspace. A copy across storage providers returns 501.
- The key needs the Viewer role on the source and the Editor role on the target.
- Storage does not check the `x-amz-copy-source-if-*` conditions.

## Example request

```http
PUT /support-tickets/b.json HTTP/1.1
Host: wspq7ws2np5l8z4.storage.beta.graphon.sh
x-amz-date: 20261007T093000Z
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Authorization: AWS4-HMAC-SHA256 Credential=<ACCESS_KEY_ID>/20261007/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=<SIGNATURE>
x-amz-copy-source: support-tickets/a.json
```

## Path parameters

- `bucket` (string, required): The bucket name.
- `key` (string, required): The object key. Keep each `/` as it is. Percent-encode the other reserved characters.

## Headers

- `x-amz-copy-source` (string, required): The source, as `{bucket}/{key}`, URL-encoded. Add `?versionId=` to copy one version.
- `x-amz-metadata-directive` (string, optional): Whether to keep the metadata of the source or to use the headers of this request. Default: `COPY`. Values: `COPY`, `REPLACE`.
- `x-amz-tagging-directive` (string, optional): Whether to keep the tags of the source. Default: `COPY`. Values: `COPY`, `REPLACE`.
- `Content-Type` (string, optional): The media type of the copy. Used with `REPLACE`.
- `Cache-Control` (string, optional): Stored with the object and returned on reads. Used with `REPLACE`.
- `Content-Disposition` (string, optional): Stored with the object and returned on reads. Used with `REPLACE`.
- `Content-Encoding` (string, optional): Stored with the object and returned on reads. Used with `REPLACE`.
- `Content-Language` (string, optional): Stored with the object and returned on reads. Used with `REPLACE`.
- `x-amz-meta-*` (string, optional): User metadata. Used with `REPLACE`.
- `x-amz-storage-class` (string, optional): Recorded with the object and shown in listings. Used with `REPLACE`.
- `x-amz-tagging` (string, optional): Tags for the object, as a URL query. For example, `team=support&tier=gold`. Used with a tagging directive of `REPLACE`.
- `x-amz-expected-bucket-owner` (string, optional): The workspace ID that must own the bucket. Another owner returns `403 AccessDenied`.

## Responses

### 200 · OK

Storage wrote the copy.

Response body:

- `CopyObjectResult` (object, required): The root element.
  - `LastModified` (string, optional): When the copy was written.
  - `ETag` (string, optional): The entity tag of the copy.

```http
HTTP/1.1 200 OK
Content-Type: application/xml
x-amz-request-id: 4F8A2C7D1E6B4093A5C2D8E1F7B3A960

<?xml version="1.0" encoding="UTF-8"?>
<CopyObjectResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <LastModified>2026-10-07T09:30:00.000Z</LastModified>
  <ETag>"9b2cf535f27731c974343645a3985328"</ETag>
</CopyObjectResult>
```

## Errors

Any request can also get the common errors on [S3 API](/api/s3.md).

### 400 · `InvalidRequest`

The `x-amz-copy-source` value is not `{bucket}/{key}`.

Send the source as `{bucket}/{key}`, URL-encoded.

### 404 · `NoSuchKey`

The source has no object at this key or version.

Check the key and the versionId.

### 404 · `NoSuchBucket`

The source bucket or the target bucket does not exist.

Check the bucket name. ListBuckets shows the buckets of the workspace.

### 403 · `AccessDenied`

The key cannot read the source or write the target, or the source is in another workspace.

Use a key of the workspace that owns the bucket, with the role above.

### 501 · `NotImplemented`

The source bucket uses another storage provider.

Copy between buckets of the same provider, or download and upload the object.

### 405 · `MethodNotAllowed`

The versionId names a delete marker, which has no bytes.

Use the versionId of a version that holds data. ListObjectVersions shows each one.

### 503 · `SlowDown`

The bucket has too many writes in progress. The response has `Retry-After: 1`.

Wait for the Retry-After time, then send the request again. SDKs retry by themselves.

## Other request languages

### aws CLI

```bash
# The graphon profile from the S3 API overview.
export AWS_PROFILE=graphon

aws s3 cp s3://support-tickets/a.json s3://support-tickets/b.json
```

### Python (boto3)

```python
# Replace wspq7ws2np5l8z4 with your workspace ID.
import os

import boto3
from botocore.config import Config

api_key = os.environ["GRAPHON_API_KEY"]
access_key_id = api_key.split("_", 1)[1].split(".")[0]

s3 = boto3.client(
    "s3",
    endpoint_url="https://wspq7ws2np5l8z4.storage.beta.graphon.sh",
    region_name="us-east-1",
    aws_access_key_id=access_key_id,
    aws_secret_access_key=api_key,
    config=Config(signature_version="s3v4", s3={"addressing_style": "path"}),
)

response = s3.copy_object(
    Bucket="support-tickets",
    Key="b.json",
    CopySource="support-tickets/a.json",
)
print(response["CopyObjectResult"]["ETag"])
```

### JavaScript (AWS SDK)

```javascript
// Replace wspq7ws2np5l8z4 with your workspace ID.
import { S3Client, CopyObjectCommand } from '@aws-sdk/client-s3';

const apiKey = process.env.GRAPHON_API_KEY;
if (!apiKey) throw new Error('Set GRAPHON_API_KEY.');
const accessKeyId = apiKey.split('_')[1].split('.')[0];

const s3 = new S3Client({
  endpoint: 'https://wspq7ws2np5l8z4.storage.beta.graphon.sh',
  region: 'us-east-1',
  forcePathStyle: true,
  credentials: { accessKeyId, secretAccessKey: apiKey },
});

const response = await s3.send(new CopyObjectCommand({
  Bucket: 'support-tickets',
  Key: 'b.json',
  CopySource: 'support-tickets/a.json',
}));
console.log(response.CopyObjectResult?.ETag);
```

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
