Search docs ⌘K
esc
Type to search across all documentation pages
Storage

Storage

Object storage backed by Cloudflare R2. Upload and download files up to 100 MB. Objects are organized into buckets scoped per project. Supports signed URLs for direct client-side uploads and downloads.

Virtual buckets: SaaSignal uses a single R2 bucket under the hood. Your "buckets" are logical namespaces stored in D1. Objects are keyed by {projectId}/{bucketId}/{objectKey}.

Create Bucket

GET
json — 201 Created
{
  "id": "sb_01JNXS...",
  "name": "assets",
  "visibility": "private",
  "created_at": "2026-03-09T12:00:00Z"
}
typescript
await ss.infra.storage.createBucket('assets', { visibility: 'private' })
  1. Open Dashboard and select your project
  2. Go to Storage in the sidebar
  3. Click Create Bucket
  4. Enter a name and choose visibility, then click Create
Body field
Type
Description
name required
string
Bucket name (1–63 chars, lowercase alphanumeric and hyphens)
visibility
string
Bucket visibility: private or public. Default private.
Error responses
400 Invalid body (validation error)
401 Unauthorized
402 Insufficient Tokens
409 Bucket name already exists
429 Rate Limited

List Buckets

GET
json — 200 OK
{
  "buckets": [
    { "id": "sb_01JNXS...", "name": "assets", "visibility": "private", "object_count": 42, "total_size": 10485760, "created_at": "2026-03-09T12:00:00Z" }
  ]
}
typescript
const { buckets } = await ss.infra.storage.listBuckets()
  1. Open Dashboard and select your project
  2. Go to Storage in the sidebar
  3. All buckets are listed with their object count and total size
Error responses
401 Unauthorized
429 Rate Limited

Get Bucket

GET
json — 200 OK
{
  "id": "sb_01JNXS...",
  "name": "assets",
  "visibility": "private",
  "object_count": 42,
  "total_size": 10485760,
  "created_at": "2026-03-09T12:00:00Z"
}
typescript
const bucket = await ss.infra.storage.getBucket('sb_...')
  1. Open Dashboard and select your project
  2. Go to Storage and click on a bucket to view its details
Path param
Type
Description
bucket_id required
string
Bucket ID
Error responses
401 Unauthorized
404 Bucket not found
429 Rate Limited

Delete Bucket

GET
typescript
await ss.infra.storage.deleteBucket('sb_...')
  1. Open Dashboard and select your project
  2. Go to Storage and click on the bucket
  3. Click Delete Bucket and confirm
Path param
Type
Description
bucket_id required
string
Bucket ID to delete
Error responses
401 Unauthorized
404 Bucket not found
409 Bucket is not empty
429 Rate Limited

Upload Object

GET
json — 201 Created
{
  "key": "images/logo.png",
  "size": 24576,
  "content_type": "image/png",
  "etag": ""d41d8cd98f00b204e9800998ecf8427e"",
  "uploaded_at": "2026-03-09T12:00:00Z"
}
typescript
await ss.infra.storage.putObject('sb_...', 'images/logo.png', file, { contentType: 'image/png' })
  1. Open Dashboard and select your project
  2. Go to Storage and open a bucket
  3. Click Upload and select a file
  4. Optionally set the object key (defaults to filename)
Path param
Type
Description
bucket_id required
string
Bucket ID
key required
string
Object key (path within the bucket, e.g. images/logo.png)
Body field
Type
Description
body required
binary
File content (max 100 MB)
Query param
Type
Description
content_type
string
MIME type of the object
Error responses
400 Invalid key or file too large
401 Unauthorized
402 Insufficient Tokens
404 Bucket not found
429 Rate Limited

List Objects

GET
json — 200 OK
{
  "objects": [
    { "key": "images/logo.png", "size": 24576, "content_type": "image/png", "etag": ""d41d8..."", "uploaded_at": "2026-03-09T12:00:00Z" }
  ],
  "next_cursor": null,
  "truncated": false
}
typescript
const { objects } = await ss.infra.storage.listObjects('sb_...', { prefix: 'images/' })
  1. Open Dashboard and select your project
  2. Go to Storage and open a bucket
  3. Browse objects or filter by prefix using the search bar
Path param
Type
Description
bucket_id required
string
Bucket ID
Query param
Type
Description
prefix
string
Filter objects by key prefix
cursor
string
Pagination cursor from a previous next_cursor response
limit
integer
Max objects to return per page. Range 1–1000, default 100.
Error responses
401 Unauthorized
404 Bucket not found
429 Rate Limited

Download Object

GET
typescript
const response = await ss.infra.storage.getObject('sb_...', 'images/logo.png')
  1. Open Dashboard and select your project
  2. Go to Storage and open a bucket
  3. Click on an object, then click Download
Path param
Type
Description
bucket_id required
string
Bucket ID
key required
string
Object key to download
Error responses
401 Unauthorized
404 Object not found
429 Rate Limited

Head Object

GET
json — 200 OK
{
  "key": "images/logo.png",
  "size": 24576,
  "content_type": "image/png",
  "etag": ""d41d8cd98f00b204e9800998ecf8427e"",
  "uploaded_at": "2026-03-09T12:00:00Z"
}
typescript
const meta = await ss.infra.storage.headObject('sb_...', 'images/logo.png')
  1. Open Dashboard and select your project
  2. Go to Storage, open a bucket, and click on an object to view its metadata
Path param
Type
Description
bucket_id required
string
Bucket ID
key required
string
Object key
Error responses
401 Unauthorized
404 Object not found
429 Rate Limited

Delete Object

GET
typescript
await ss.infra.storage.deleteObject('sb_...', 'images/logo.png')
  1. Open Dashboard and select your project
  2. Go to Storage, open a bucket, and click on the object
  3. Click Delete and confirm
Path param
Type
Description
bucket_id required
string
Bucket ID
key required
string
Object key to delete
Error responses
401 Unauthorized
404 Object not found
429 Rate Limited

Generate Signed URL

GET
json — 200 OK
{
  "url": "https://storage.saasignal.saastemly.com/sb_.../images/logo.png?X-Amz-Signature=...",
  "expires_at": "2026-03-09T13:00:00Z"
}
typescript
const { url } = await ss.infra.storage.signedUrl('sb_...', 'images/logo.png', { method: 'PUT', expires_in: 3600 })
  1. Open Dashboard and select your project
  2. Go to Storage, open a bucket, and click on an object
  3. Click Signed URL, configure method and expiry, then copy the URL
Path param
Type
Description
bucket_id required
string
Bucket ID
key required
string
Object key
Body field
Type
Description
method
string
HTTP method the signed URL permits: GET or PUT. Default GET.
expires_in
integer
URL validity in seconds. Range 1–86400, default 3600 (1 hour).
Error responses
401 Unauthorized
404 Bucket not found
429 Rate Limited

Consume Signed Download URL

GET
/infra/storage/signed/:token
typescript
const response = await ss.infra.storage.downloadSignedUrl(signedUrl.url)
  1. Generate a signed download URL from Storage
  2. Paste the resulting URL into a new tab to validate public download access, or call the underlying route from API Console
Path param
Type
Description
token required
string
Signed URL token
Query param
Type
Description
expires required
string
Signed expiry timestamp embedded in the URL
p required
string
Signed project payload
b required
string
Signed bucket payload
k required
string
Signed object key payload
m required
string
Signed HTTP method. Must be GET.
Error responses
403 Signed URL is invalid or expired
404 Object not found
429 Rate Limited

Consume Signed Upload URL

PUT
/infra/storage/signed/:token
typescript
await ss.infra.storage.uploadSignedUrl(signedUrl.url, file, { contentType: 'image/png' })
  1. Generate a signed upload URL from Storage
  2. Use API Console in raw mode to send file bytes through the public signed upload route
  3. Or hand the signed URL to any browser client that should upload directly without exposing your API key
Path param
Type
Description
token required
string
Signed URL token
Query param
Type
Description
expires required
string
Signed expiry timestamp embedded in the URL
p required
string
Signed project payload
b required
string
Signed bucket payload
k required
string
Signed object key payload
m required
string
Signed HTTP method. Must be PUT.
ct
string
Optional content type embedded in the signed URL
Body field
Type
Description
body required
binary
Raw file bytes to upload
Error responses
403 Signed URL is invalid or expired
429 Rate Limited