Using S3 Clients
FoundryDB Files speaks the S3 API with virtual-host bucket addressing. Any S3-compatible client works by pointing it at the regional endpoint and supplying a scoped access key.
Before you start, retrieve your bucket coordinates from GET /file-services/{id} and mint an access key from POST /file-services/{id}/keys. See Buckets and Keys.
AWS CLI
Configure the CLI with your scoped key:
export AWS_ACCESS_KEY_ID=EXAMPLEkeyid
export AWS_SECRET_ACCESS_KEY=EXAMPLEsecret
export AWS_DEFAULT_REGION=europe-1
All AWS CLI commands require --endpoint-url pointing at the regional endpoint:
# Upload a file
aws s3 cp report.pdf s3://files-f0e1d2c3/reports/q1.pdf \
--endpoint-url https://eu.files.foundrydb.com
# Download a file
aws s3 cp s3://files-f0e1d2c3/reports/q1.pdf ./q1.pdf \
--endpoint-url https://eu.files.foundrydb.com
# List objects under a prefix
aws s3 ls s3://files-f0e1d2c3/reports/ \
--endpoint-url https://eu.files.foundrydb.com
# Delete an object
aws s3 rm s3://files-f0e1d2c3/reports/q1.pdf \
--endpoint-url https://eu.files.foundrydb.com
AWS SDK for JavaScript / TypeScript
import { S3Client, PutObjectCommand, GetObjectCommand } from "@aws-sdk/client-s3";
import { readFileSync } from "fs";
const s3 = new S3Client({
region: "europe-1",
endpoint: "https://eu.files.foundrydb.com",
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY_ID!,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY!,
},
// Required: virtual-host addressing is the default in AWS SDK v3 but must
// be confirmed when using a custom endpoint.
forcePathStyle: false,
});
const BUCKET = "files-f0e1d2c3";
// Upload
await s3.send(new PutObjectCommand({
Bucket: BUCKET,
Key: "uploads/photo.jpg",
Body: readFileSync("./photo.jpg"),
ContentType: "image/jpeg",
}));
// Download
const response = await s3.send(new GetObjectCommand({
Bucket: BUCKET,
Key: "uploads/photo.jpg",
}));
const body = await response.Body?.transformToByteArray();
AWS SDK for Python (boto3)
import boto3
import os
s3 = boto3.client(
"s3",
region_name="europe-1",
endpoint_url="https://eu.files.foundrydb.com",
aws_access_key_id=os.environ["S3_ACCESS_KEY_ID"],
aws_secret_access_key=os.environ["S3_SECRET_ACCESS_KEY"],
)
BUCKET = "files-f0e1d2c3"
# Upload
s3.upload_file("report.pdf", BUCKET, "reports/q1.pdf")
# Download
s3.download_file(BUCKET, "reports/q1.pdf", "q1_local.pdf")
# List objects under a prefix
response = s3.list_objects_v2(Bucket=BUCKET, Prefix="reports/")
for obj in response.get("Contents", []):
print(obj["Key"], obj["Size"])
Presigned URLs
Presigned URLs let a browser or third-party upload or download a single object directly, without credentials. The URL is signed by the platform and expires after a configurable duration.
Issue a Presigned PUT (browser upload)
curl -u admin:password -X POST \
https://api.foundrydb.com/file-services/{id}/presign \
-H "Content-Type: application/json" \
-d '{
"method": "PUT",
"key": "uploads/avatar.png",
"content_type": "image/png",
"expires_seconds": 300
}'
Response:
{
"url": "https://files-f0e1d2c3.eu.files.foundrydb.com/uploads/avatar.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-...",
"method": "PUT",
"expires_at": "2026-06-22T15:35:00Z"
}
The browser uploads directly to that URL with the matching Content-Type header:
curl -X PUT \
-H "Content-Type: image/png" \
--data-binary @avatar.png \
"https://files-f0e1d2c3.eu.files.foundrydb.com/uploads/avatar.png?X-Amz-Algorithm=..."
Issue a Presigned GET (time-limited download)
curl -u admin:password -X POST \
https://api.foundrydb.com/file-services/{id}/presign \
-H "Content-Type: application/json" \
-d '{
"method": "GET",
"key": "reports/q1.pdf",
"expires_seconds": 3600
}'
Presign Request Fields
| Field | Required | Description |
|---|---|---|
method | Yes | GET, PUT, HEAD, or DELETE (uppercase) |
key | Yes | Object key, up to 1024 characters. Must not contain .. |
expires_seconds | No | URL lifetime in seconds. Default 900 (15 minutes), maximum 604800 (7 days) |
content_type | No | When set on a PUT, the upload must send the matching Content-Type header |
Every issued URL is recorded in the platform audit log with the object key, method, expiry, and actor. The signed request itself runs directly against the object storage endpoint and is not observable by the platform.
PUT presigning returns 403 while the service is over its hard storage quota. GET presigning is always available.
Console Object Listing
The platform exposes a paginated object listing for console use. It does not replace S3 client access for bulk operations.
# List objects, optionally filtered by prefix
curl -u admin:password \
"https://api.foundrydb.com/file-services/{id}/objects?prefix=reports/&max=50"
# Continue from a cursor
curl -u admin:password \
"https://api.foundrydb.com/file-services/{id}/objects?prefix=reports/&cursor=NEXT_CURSOR"
Delete one object via the console endpoint:
curl -u admin:password -X DELETE \
"https://api.foundrydb.com/file-services/{id}/objects?key=reports/old.pdf"
What's Next
- Attach to Apps — skip manual credential management by attaching the bucket to a hosted app