Browse documentation

Route: /p/:id/storage. Source: apps/studio/src/features/storage, backed by apps/server/controllers/admin-storage.controller.ts.

Buckets

ActionEndpoint
ListGET /admin/storage/buckets
CreatePOST /admin/storage/buckets
DeleteDELETE /admin/storage/buckets/:slug?force=<bool>
Deletion job statusGET /admin/storage/bucket-deletions/:id

Deleting a non-empty bucket needs force. That does not run inline: it creates a BucketDeletionJob that the deletion scheduler drains with a lease, tracking totalObjects / deletedObjects / attempts. The screen polls the job id for progress, and the job survives a restart — see the data model.

Objects

ActionEndpoint
List (paged)GET /admin/storage/buckets/:bucket/files?… → { rows, total }
UploadPOST /admin/storage/files (multipart)
Signed URLGET /admin/storage/files/:id/url
DeleteDELETE /admin/storage/files/:id

Upload limits come from MAX_UPLOAD_MB (default 25) and the storage.limits settings group; per-bucket limits are clamped to the global ceiling.

CORS

GET    /admin/storage/buckets/:bucket/cors
PUT    /admin/storage/buckets/:bucket/cors
DELETE /admin/storage/buckets/:bucket/cors

A BucketCors row holds allowedOrigins, allowedMethods, allowedHeaders and maxAgeSec (default 3600). It is enforced by middlewares/bucketCors.middleware.ts on the public storage routes — independent of the project-wide server.cors group, which is edited in one place only: Project Settings → Security.

Scoped tokens

GET    /admin/storage/buckets/:bucket/tokens
POST   /admin/storage/buckets/:bucket/tokens
DELETE /admin/storage/buckets/:bucket/tokens/:id

A BucketToken carries permissions[] (read, write, delete) and an optional expiresAt. It authenticates the public storage API (bucketTokenGuard) without a user session:

POST   /api/storage/:slug/upload      requires write
GET    /api/storage/:slug/files/:id
DELETE /api/storage/:slug/files/:id   requires delete
Warning

Bucket tokens are bearer credentials scoped to one bucket. Through the Console proxy, listing or creating them requires secrets.manage. Prefer short expiresAt values for anything handed to a client.

Per-bucket webhooks

GET    /admin/storage/buckets/:bucket/webhooks
POST   /admin/storage/buckets/:bucket/webhooks
DELETE /admin/storage/buckets/:bucket/webhooks/:id

BucketWebhook rows are url + events[], separate from project-level event webhooks.

Public vs private

Only the public bucket (S3_PUBLIC_BUCKET) is served statically. Private objects are reachable only through the authenticated file endpoints — there is no guessable static URL for them. Backend, drivers (local / s3) and configuration: Storage.

Protocol and image endpoints

The project storage service also exposes the S3-compatible protocol when S3 support is configured, and resumable uploads through TUS 1.0.0. These are data-plane interfaces, not additional Studio bucket records; quotas, visibility and bucket policies still come from the same project configuration.

Image transforms are served by /storage/v1/render/image. The SDK can request dimensions, resize mode, quality and output format through download, getPublicUrl, or createSignedUrl; SVG inputs are not transformable and transformed signed URLs expire.