S3 API
Use S3 tools and SDKs with an API key: the endpoint, signing, client setup, every S3 operation that storage supports, and the common errors.
Graphon Storage speaks the Amazon S3 API. Point an S3 tool or SDK at your workspace S3 endpoint, and sign with an API key from that workspace. Each operation below has its own page with its parameters, responses, errors, and samples.
Connection
| Setting | Value |
|---|---|
| S3 endpoint | https://wspq7ws2np5l8z4.storage.beta.graphon.sh |
| JSON API | https://storage.beta.graphon.sh |
| Region | us-east-1 |
| Addressing | Path style: /{bucket}/{key} |
| Signature | AWS Signature Version 4, in a header or a presigned URL |
| Access key ID | The part of the API key between the _ and the . |
| Secret access key | The whole API key |
Replace wspq7ws2np5l8z4 in every example with your workspace ID. The key must belong to the workspace in the hostname. The bare storage host serves the JSON API only. Use the workspace host for S3, including public object links and presigned requests.
us-east-1 is only the signing region. Your data stays in the location that you chose for the bucket. A key covers every bucket in its workspace, and each operation needs the role on its page. See Authentication & keys for how to get the credentials and presign a URL.
Bucket names and rename
Names are unique within one workspace and are case-sensitive. Use 1–255 ASCII letters, digits, periods, underscores, or hyphens. The exact names . and .. are invalid. Another workspace can use the same name.
Rename through the JSON API or graphon buckets rename. S3 has no RenameBucket operation. The bucket ID and data stay unchanged. Update name-based URLs and client settings after a rename. See Renaming buckets.
Set up a client
The aws CLI reads the graphon profile. The SDK samples read the key from GRAPHON_API_KEY. Each operation page uses the same setup.
~/.aws/config
# Replace wspq7ws2np5l8z4 with your workspace ID.
# ~/.aws/config
[profile graphon]
credential_process = graphon s3-credentials --format process
endpoint_url = https://wspq7ws2np5l8z4.storage.beta.graphon.sh
region = us-east-1
s3 =
addressing_style = pathaws CLI
aws s3 ls --profile graphon
aws s3 cp ./checkout-timeout.json s3://support-tickets/incidents/ --profile graphonboto3
# 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"}),
)
print(s3.list_objects_v2(Bucket="support-tickets", Prefix="incidents/"))AWS SDK for JavaScript
import { ListObjectsV2Command } from '@aws-sdk/client-s3';
// Replace wspq7ws2np5l8z4 with your workspace ID.
import { S3Client } 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 },
});
console.log(await s3.send(new ListObjectsV2Command({
Bucket: 'support-tickets',
Prefix: 'incidents/',
})));rclone
# Replace wspq7ws2np5l8z4 with your workspace ID.
# ~/.config/rclone/rclone.conf
[graphon]
type = s3
provider = Other
endpoint = https://wspq7ws2np5l8z4.storage.beta.graphon.sh
region = us-east-1
force_path_style = true
env_auth = true
# Then, with GRAPHON_API_KEY set, this sets AWS_ACCESS_KEY_ID and
# AWS_SECRET_ACCESS_KEY for rclone:
# eval "$(graphon s3-credentials)"
# rclone ls graphon:support-ticketsOperations
Storage supports these operations. A partial operation supports the common case. Its page lists what differs from Amazon S3.
Buckets
- GETListBucketsLists the buckets in the workspace of the key.
/ - PUTCreateBucketCreates a bucket in the workspace of the key.
/{bucket} - HEADHeadBucketChecks that a bucket exists and that the key can read it.
/{bucket} - DELETEDeleteBucketDeletes an empty bucket.
/{bucket} - GETGetBucketLocationReturns the signing region of a bucket.
/{bucket}?location
Objects
- PUTPutObjectWrites an object.
/{bucket}/{key} - GETGetObjectReads an object.
/{bucket}/{key}Partial - HEADHeadObjectReads the headers of an object, with no body.
/{bucket}/{key} - PUTCopyObjectCopies an object to another key.
/{bucket}/{key}Partial - DELETEDeleteObjectDeletes an object, or one version of an object.
/{bucket}/{key} - POSTDeleteObjectsDeletes up to 1,000 objects in one request.
/{bucket}?delete - GETListObjectsV2Lists the objects in a bucket, one page at a time.
/{bucket}?list-type=2 - GETListObjectsLists the objects in a bucket with marker pagination.
/{bucket}
Multipart uploads
- POSTCreateMultipartUploadStarts a multipart upload and returns its upload ID.
/{bucket}/{key}?uploads - PUTUploadPartUploads one part of a multipart upload.
/{bucket}/{key}?partNumber&uploadId - PUTUploadPartCopyCopies a byte range of an object into one part of a multipart upload.
/{bucket}/{key}?partNumber&uploadIdPartial - POSTCompleteMultipartUploadJoins the uploaded parts into one object.
/{bucket}/{key}?uploadId - DELETEAbortMultipartUploadStops a multipart upload and deletes its parts.
/{bucket}/{key}?uploadId - GETListPartsLists the parts of a multipart upload.
/{bucket}/{key}?uploadId - GETListMultipartUploadsLists the multipart uploads in a bucket that are not complete.
/{bucket}?uploads
Versioning & retention
- GETGetBucketVersioningReturns the versioning state of a bucket.
/{bucket}?versioning - PUTPutBucketVersioningTurns versioning on or off for a bucket.
/{bucket}?versioningPartial - GETListObjectVersionsLists the versions and delete markers in a bucket.
/{bucket}?versions - GETGetObjectLockConfigurationReturns the object lock settings of a bucket.
/{bucket}?object-lock - PUTPutObjectLockConfigurationTurns on object lock, and sets the default retention for new versions.
/{bucket}?object-lockPartial - GETGetObjectRetentionReturns the retention of one object version.
/{bucket}/{key}?retention - PUTPutObjectRetentionSets or extends the retention of one object version.
/{bucket}/{key}?retentionPartial
Object tags
Common errors
An error has an XML body with a Code and a Message. Every response has an x-amz-request-id header. Send it to support when you report a problem. Any request can get these errors. Each operation page lists its own errors.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 403 | AccessDenied | The request has no signature, the presigned URL expired, or the role of the key is too low. | Sign the request, presign a new URL, or use a key with a higher role. |
| 403 | AccessDenied | The key belongs to a workspace other than the one in the hostname, or that workspace is unknown or suspended. | Use the S3 endpoint of the workspace that owns the key. |
| 400 | InvalidBucketName | The bucket name in the path breaks the naming rules. | Use 1 to 255 ASCII letters, digits, periods, underscores, or hyphens. A name cannot be . or ... |
| 403 | InvalidAccessKeyId | No key has this access key ID, or the key was revoked. | Check the access key ID. It is the part of the API key between _ and .. |
| 403 | ExpiredToken | The key has expired. | Create a new key. |
| 403 | SignatureDoesNotMatch | The signature does not match the request. | Use the whole API key as the secret access key, sign for us-east-1, and use path-style addressing. |
| 403 | RequestTimeTooSkewed | The request time is too far from the time of the server. | Set the clock of the client with NTP. |
| 403 | AuthorizationQueryParametersError | A presigned URL has an X-Amz-Date or X-Amz-Expires value that is not valid. | Presign the URL again. |
| 400 | InvalidRequest | A signed request has no x-amz-content-sha256 header. | Send the header. SDKs and the aws CLI send it by themselves. |
| 400 | InvalidURI | The path or the query has a % escape that does not decode. | Percent-encode the key and the query values correctly. |
| 405 | MethodNotAllowed | The path does not accept this HTTP method. | Check the method and the path of the operation. |
| 413 | EntityTooLarge | The body is larger than 200 MiB. | Use a multipart upload. |
| 501 | NotImplemented | The request uses an S3 feature that storage does not support. | See Not supported below. |
| 503 | SlowDown | The bucket has too many writes in progress. | Retry after the Retry-After time. SDKs retry by themselves. |
| 500 | InternalError | Storage could not handle the request. | Retry with backoff. Send the x-amz-request-id to support if it continues. |
Browser requests
Storage answers a CORS preflight (OPTIONS) on any S3 path with 204. It allows every origin and header, and the GET, PUT, POST, DELETE, and HEAD methods. A preflight reads and changes nothing. A browser can use a presigned URL, or read an object from a public-read bucket with no signature.
Not supported
These requests return 501 NotImplemented. An S3 request to the bare storage host returns 404 with a JSON error. Storage ignores the other S3 features that it does not support. Each operation page names the parameters that it ignores.
| Request | Result |
|---|---|
GET /{bucket}?website | Website, CORS, and lifecycle configuration return 501. |
GET /{bucket}/{key}?acl | Object ACLs return 501. Use the bucket permission in the JSON API. |
GET /{bucket}/{key}?legal-hold | Legal hold returns 501. |
POST /{bucket}/{key}?select | S3 Select returns 501. |
PUT /{bucket}/{key} with x-amz-server-side-encryption-customer-algorithm | SSE-C returns 501. Providers encrypt every object at rest. |
Larger files