The Object storage quick start shows how to talk to a bucket with an S3 client. This page is the management side: creating the bucket and its keys and reading usage, all through the Zaroz API instead of the console. Files themselves still go through the S3 endpoint (or the signed links below). The samples use the helper from Using the Zaroz API.
Object storage may be absent on some deployments: check object_storage_buckets in what the deployment offers. Everything here is addressed by the subscription id of your object storage plan, and writing needs the authority to buy for the organization (Owner or Admin).
Create the bucket
One bucket per subscription. cap_bytes is the storage cap (1 GiB to 100 TiB, default 100 GiB); region_id defaults to the region of the plan.
import { call } from "./zaroz";const b = await call("POST", `/subscriptions/${SUB}/bucket`, { label: "site-assets", cap_bytes: 100 * 1024 ** 3 });const { endpoint, bucket_name } = b.body.data;console.log(endpoint, bucket_name); // the two values your S3 client needs
Python
Code
from zaroz import call_, b = call("POST", f"/subscriptions/{SUB}/bucket", {"label": "site-assets", "cap_bytes": 100 * 1024**3})print(b["data"]["endpoint"], b["data"]["bucket_name"]) # the two values your S3 client needs
A second bucket answers 409, and an inactive subscription 422. bucket_name is the real name to use in S3 requests (not your label). access says what the data plane allows right now: open, read_only (over the cap), suspended or deleted. GET /subscriptions/{id}/bucket returns the same object (404 when there is no bucket yet).
Absent fields are left alone. A cap change and a public-read change reach the data plane at once. With public_read on, anyone can download an object at public_url/KEY without a key (listing and writing still need one); turn it off to close it. Not allowed once the bucket is scheduled for deletion (422).
Access keys
A key is read_only or read_write and works only on this bucket. At most 20 active keys per bucket (429 past it).
const k = await call("POST", `/subscriptions/${SUB}/bucket/keys`, { name: "ci-upload", scope: "read_write" });console.log(k.body.data.access_key_id, k.body.secret_access_key); // the secret is shown ONCEawait call("DELETE", `/subscriptions/${SUB}/bucket/keys/${k.body.data.id}`); // revoke
Python
Code
_, k = call("POST", f"/subscriptions/{SUB}/bucket/keys", {"name": "ci-upload", "scope": "read_write"})print(k["data"]["access_key_id"], k["secret_access_key"]) # the secret is shown ONCEcall("DELETE", f"/subscriptions/{SUB}/bucket/keys/{k['data']['id']}") # revoke
The secret is returned only in this response; listing never shows it, and Zaroz cannot show it again. Use access_key_id and secret_access_key as AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in the quick start. Revoking stops that key at once.
const u = await call("GET", `/subscriptions/${SUB}/bucket/usage`);console.log(u.body.data);
Code
_, u = call("GET", f"/subscriptions/{SUB}/bucket/usage")print(u["data"])
Without a window you get this month so far (at most 93 days): hourly stored-byte and traffic samples, the average stored size in GiB-months and the bytes sent out, which are the two billed meters. Uploads are free.
Browse and sign transfers
If you do not want a key at all (for instance a web back end that hands links to browsers), ask the API to sign a link per object. Listing is metadata only and not billed.
curl
Code
O=/subscriptions/$SUBSCRIPTION/bucket/objectscurl -sS -G "$ZAROZ_API$O" -H "$AUTH" --data-urlencode "prefix=photos/" --data-urlencode "limit=100"curl -sS -X POST "$ZAROZ_API$O/upload" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"key": "photos/a.jpg", "size_bytes": 482344, "content_type": "image/jpeg"}'# up to 64 MiB: a signed PUT of exactly that many bytes. Larger (up to 625 GiB): a multipart upload.curl -sS -X POST "$ZAROZ_API$O/download" -H "$AUTH" -H "Content-Type: application/json" \ -d '{"key": "photos/a.jpg", "save_as": "holiday.jpg"}'# a 15-minute signed GET
TypeScript
Code
const O = `/subscriptions/${SUB}/bucket/objects`;const start = await call("POST", `${O}/upload`, { key: "photos/a.jpg", size_bytes: file.byteLength, content_type: "image/jpeg" });// For a single PUT upload, send exactly size_bytes bytes to the signed URL in start.body.dataconst dl = await call("POST", `${O}/download`, { key: "photos/a.jpg" });console.log(dl.body.data); // the signed URL, valid for 15 minutes
Python
Code
O = f"/subscriptions/{SUB}/bucket/objects"_, start = call("POST", f"{O}/upload", {"key": "photos/a.jpg", "size_bytes": len(data), "content_type": "image/jpeg"})# For a single PUT upload, send exactly size_bytes bytes to the signed URL in start["data"]_, dl = call("POST", f"{O}/download", {"key": "photos/a.jpg"})print(dl["data"]) # the signed URL, valid for 15 minutes
Downloads go straight to the storage endpoint and are billed as egress like any S3 client, previews included. Larger uploads are multipart: POST .../objects/upload/parts signs each part, .../upload/complete finishes and .../upload/abort cancels. POST .../objects/delete deletes objects and POST .../objects/folders creates a folder marker. Uploads are refused while the bucket is over its cap or suspended. For heavy or repeated use, an access key and an S3 client are simpler than signing each object.
The keys stop working immediately and the slot frees, but the data stays for a seven-day recovery window (purge_at shows the date) before it is purged for good.