Buckets
List, create, read, configure, and delete buckets, the top-level containers that hold a workspace's folders and files.
On this page
A bucket is a top-level container in a workspace, such as Marketing or Product media. Folders and files live inside it, and its settings decide the default visibility of new files and which uploads it accepts. Use these endpoints to find a bucket ID for uploads, create buckets from a provisioning script, apply an upload policy, or tear a bucket down.
Bucket routes live under /api/assets for historical reasons: in the API, a bucket is an asset of kind bucket, and a file is an asset of kind object. Bucket IDs and file asset IDs are both UUIDs, so keep track of which one you hold.
The JavaScript examples assume const steadylink = new SteadyLink({ apiKey: process.env.STEADYLINK_API_KEY! }) and the Python examples assume client = SteadyLink(api_key=os.environ["STEADYLINK_API_KEY"]).
Endpoints#
| Operation | Method and path | Scope |
|---|---|---|
| List buckets | GET /api/assets/ | assets:read |
| Create a bucket | POST /api/assets/ | assets:write |
| Get a bucket | GET /api/assets/{bucket_id} | assets:read |
| Set a bucket's default privacy | POST /api/assets/{bucket_id}/privacy | assets:write |
| Update bucket settings | POST /api/assets/{bucket_id}/settings | assets:write |
| Delete a bucket | DELETE /api/assets/{bucket_id} | assets:write |
Operations#
List buckets#
/api/assets/assets:readReturns the workspace's buckets, newest first.
Query parameters
limitintegerDefault20- Number of buckets per page. Values outside 1 to 100 are clamped into that range.
cursorstring- The
nextCursorfrom the previous page. It is thecreatedAtof the last bucket returned. A value that is not a timestamp is ignored and the first page is returned.
curl "https://api.steadylink.io/api/assets/?limit=100" \
-H "X-API-Key: $STEADYLINK_API_KEY"const { items, nextCursor } = await steadylink.listBuckets(100);
// Or look one up by ID, slug, or name (case-insensitive)
const marketing = await steadylink.findBucket("marketing");page = client.list_buckets(limit=100)
marketing = next(b for b in page["items"] if b.get("slug") == "marketing")steadylink ls{
"items": [
{
"id": "7c1e4b2a-5d3f-4e8a-9b61-2f0c8d4a7e15",
"name": "Marketing",
"slug": "marketing",
"kind": "bucket",
"createdAt": "2026-08-17T18:31:00.412093",
"isPrivate": false
},
{
"id": "c2a7f0e9-3b14-4d86-8e5a-0f9d1b3c7a42",
"name": "Product media",
"slug": null,
"kind": "bucket",
"createdAt": "2026-07-02T09:12:45.003118",
"isPrivate": true
}
],
"nextCursor": "2026-07-02T09:12:45.003118"
}Response fields
items[].iduuid- Bucket ID. Use it as
bucketIdwhen you create uploads. items[].namestring- Display name.
items[].slugstring | null- Optional short identifier. The CLI and
findBucket()accept it in place of the ID. items[].isPrivateboolean- The bucket's default privacy. It decides delivery only for files whose visibility is
inherit. nextCursorstring | null- Pass as
cursorfor the next page. It is set on every non-empty page, including the last one.
nextCursor alone does not tell you that more pages exist. Stop when a page returns fewer buckets than limit, or none.
Create a bucket#
/api/assets/assets:writeBody
namestringRequired- Display name, for example
Product media. slugstring- Optional short identifier, up to 300 characters, for example
product-media. Slugs are unique across all of SteadyLink, not only your workspace.
curl -X POST "https://api.steadylink.io/api/assets/" \
-H "X-API-Key: $STEADYLINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Product media", "slug": "acme-product-media" }'const { id } = await steadylink.createBucket("Product media", "acme-product-media");bucket = client.request("POST", "/api/assets/", {"name": "Product media", "slug": "acme-product-media"})
bucket_id = bucket["id"]{ "id": "c2a7f0e9-3b14-4d86-8e5a-0f9d1b3c7a42" }A new bucket starts private or public according to the workspace's default visibility. Workspaces without a stored default start new buckets private, so nothing is published by accident. Change it afterwards with Set a bucket's default privacy or Update bucket settings.
Get a bucket#
/api/assets/{bucket_id}assets:readReturns the bucket with its settings. A bucket in another workspace returns 403; an unknown ID returns 404 Asset not found.
curl "https://api.steadylink.io/api/assets/7c1e4b2a-5d3f-4e8a-9b61-2f0c8d4a7e15" \
-H "X-API-Key: $STEADYLINK_API_KEY"const bucket = await steadylink.getBucket("7c1e4b2a-5d3f-4e8a-9b61-2f0c8d4a7e15");bucket = client.get_asset("7c1e4b2a-5d3f-4e8a-9b61-2f0c8d4a7e15"){
"id": "7c1e4b2a-5d3f-4e8a-9b61-2f0c8d4a7e15",
"name": "Marketing",
"slug": "marketing",
"createdAt": "2026-08-17T18:31:00.412093",
"isPrivate": false,
"settings": {
"default_public": true,
"max_upload_mb": 50,
"allowed_mime_prefixes": ["image/", "application/pdf"]
}
}settings is null until a setting has been saved. Its keys are described under Update bucket settings.
Set a bucket's default privacy#
/api/assets/{bucket_id}/privacyassets:writeQuery parameters
is_privatebooleanRequiredtruemakes the bucket private,falsemakes it public.
curl -X POST "https://api.steadylink.io/api/assets/7c1e4b2a-5d3f-4e8a-9b61-2f0c8d4a7e15/privacy?is_private=true" \
-H "X-API-Key: $STEADYLINK_API_KEY"{ "id": "7c1e4b2a-5d3f-4e8a-9b61-2f0c8d4a7e15", "is_private": true }This changes the bucket's own privacy flag, which governs delivery only for files whose visibility is inherit. Files uploaded through the API and dashboard receive an explicit public or private visibility when they are created, so flipping this flag does not change them. To make every file in a bucket private, set each file's visibility with Set file visibility. To change what new uploads get, set default_public in the bucket settings.
Update bucket settings#
/api/assets/{bucket_id}/settingsassets:writeSend a JSON object with the settings to change. Settings you omit keep their current values, and keys not listed below are dropped. Setting keys are snake_case.
Body
default_publicboolean- Visibility given to new files in this bucket:
truefor public,falsefor private. Without it, new files follow the workspace default. max_upload_mbinteger- Largest file this bucket accepts, in MiB. It can only tighten the plan's upload limit, never raise it.
allowed_mime_prefixesstring[]- Accept only files whose detected type starts with one of these prefixes, for example
["image/", "application/pdf"]. Send[]to allow all types. allowed_extensionsstring[]- Accept only these filename extensions, with or without the leading dot, for example
["webp", ".pdf"]. Send[]to allow all. block_mime_mismatchbooleanDefaulttrue- Reject an upload when the content type stored with its bytes and the type detected from the bytes are both concrete and belong to different families, for example
image/pngbytes that are actually a ZIP archive. Bytes sent to a presigned upload URL are always stored asapplication/octet-stream, which never conflicts, so this check mainly affects other upload paths. html_download_onlyboolean- Serve HTML files as downloads (
Content-Disposition: attachment) instead of rendering them. Downloads are the default. svg_download_onlyboolean- Serve SVG files as downloads instead of rendering them inline. Downloads are the default.
pdf_sanitize_enabledboolean- Serve a sanitized copy of PDFs with active content removed.
curl -X POST "https://api.steadylink.io/api/assets/7c1e4b2a-5d3f-4e8a-9b61-2f0c8d4a7e15/settings" \
-H "X-API-Key: $STEADYLINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"default_public": true,
"max_upload_mb": 50,
"allowed_mime_prefixes": ["image/", "application/pdf"]
}'client.request(
"POST",
"/api/assets/7c1e4b2a-5d3f-4e8a-9b61-2f0c8d4a7e15/settings",
{"default_public": True, "max_upload_mb": 50, "allowed_mime_prefixes": ["image/", "application/pdf"]},
){
"updated": true,
"settings": {
"default_public": true,
"max_upload_mb": 50,
"allowed_mime_prefixes": ["image/", "application/pdf"]
}
}Upload policies are checked when an upload is finalized, after the bytes have been sent. A file that breaks the policy ends in the blocked state with the error code policy and a message such as File type not allowed by bucket policy. Check types and sizes before uploading if you want to fail faster. See Uploads.
Delete a bucket#
/api/assets/{bucket_id}assets:writeDeletes the bucket, every file in it, every retained revision, cached image variants, and folder markers. Storage used by those revisions is released from the workspace's usage. Every stable link to a file in the bucket stops working.
curl -X DELETE "https://api.steadylink.io/api/assets/c2a7f0e9-3b14-4d86-8e5a-0f9d1b3c7a42" \
-H "X-API-Key: $STEADYLINK_API_KEY"await steadylink.deleteBucket("c2a7f0e9-3b14-4d86-8e5a-0f9d1b3c7a42");client.request("DELETE", "/api/assets/c2a7f0e9-3b14-4d86-8e5a-0f9d1b3c7a42"){ "deleted": true }Passing a file's asset ID instead of a bucket ID returns 404 Bucket not found. To delete one file, use Delete a file.