Serenities AI™
Guides
GuideFiles & Storage

Files & Storage

Upload, manage, and serve files from secure cloud storage. Control who can access files with access rules.

Files are stored securely in the cloud. Each file gets a temporary signed download URL. Access is controlled by your app's access rules.

SDK Operations

Use the SDK in your published app pages to work with files:

// Import from the SDK
import { files } from '../api/sdk';

// List files — { files: [...] }; moreNotJudged: true when the files' access-rule functions ran
// 100 times in this listing and the rest were not checked
const fileList = await files.list();

// Upload a file (default: uploader + app owner can read)
const uploaded = await files.upload(file);

// Upload with access rules — no separate setAccessRules call needed
await files.upload(file, {
  accessRules: { read: [{ type: 'creator' }, { type: 'has_role', role: 'admin' }] },
});

// Get file metadata (also returns a 1-hour presigned download URL)
const fileMeta = await files.get(fileId);

// Get a download URL (synchronous — returns a string)
const url = files.downloadUrl(fileId);

// Get an inline URL (synchronous)
const inlineUrl = files.url(fileId);

// Delete a file
await files.delete(fileId);

files.get() returns a raw, shareable presigned URL that bypasses access rules once obtained — it's valid for 1 hour regardless of who holds it. For gated content, prefer files.url() / files.downloadUrl(), which re-check access rules on every request. Reserve files.get() for metadata or large direct-download cases.

Folders

Organize files into folders for better management. Create, rename, and delete folders from the dashboard or via the SDK.

Folder operations:

  • Create folders and subfolders
  • Move files between folders
  • Rename folders
  • Delete folders (and their contents)

Setting access rules on a folder applies them to its files, and files uploaded or moved into the folder later inherit the folder's rules automatically; a file's own rules always win.

Access Control

Set rules for who can upload, view, and delete files. The same rule types from Access Control apply.

A file belongs to the site it was uploaded through, and to any other site you add to it. Only the members of those sites count for "Signed-in users", roles, plans and profile rules. Public has two choices: Anyone on this site (only through the file's own sites) and Anyone, everywhere (through any of your sites and the file's link). A file that belongs to no site yet (uploaded from Drive) can't be made public without a choice: pick its sites, or choose Anyone, everywhere.

FeatureDescription
Creator trackingEach upload records who uploaded the file. Use creator rules to restrict access.
Password protectionOptionally require a password to download a file.
Expiry datesSet a date after which the file can no longer be accessed.
Download restrictionAllow viewing only (inline), preventing downloads.

File Metadata

Each file stores metadata automatically:

FieldDescription
idUnique file identifier
nameOriginal file name
mimeTypeFile type (e.g., image/png, application/pdf)
sizeFile size in bytes
createdAtUpload timestamp
creatorIdID of the user who uploaded the file

Files in Backend Functions

Access files from backend functions using ctx.files:

// List files — returns { files, total }, not a bare array
const { files, total } = await ctx.files.list({ limit: 50, offset: 0 });
// In a visitor's run: at most 100 access-rule function runs per listing, then it stops with moreNotJudged: true

// Upload a small file (content must be base64-encoded, <10MB)
const { fileId } = await ctx.files.upload('report.csv', base64Content, 'text/csv');

// For files 10MB+, get a one-shot upload link instead (sizeBytes is required; folderId is optional),
// then PUT the raw bytes to it — the response to that PUT carries the fileId
const { uploadUrl } = await ctx.files.getUploadUrl('video.mp4', 'video/mp4', { sizeBytes: 12_400_000 });

// Get download URL (presigned, valid 1 hour)
const { url } = await ctx.files.getUrl(fileId);

// Delete a file
await ctx.files.delete(fileId);

// ctx.service.files — same methods, deliberately bypasses file access rules
// (still scoped to this project). Use for owner/admin-style operations.
await ctx.service.files.list();

Download URLs from ctx.files.getUrl() are temporary, signed, and expire after 1 hour.

When a function runs for a visitor or a signed-in member, ctx.files.list, getUrl and delete follow each file's own rules for that person, like the files.* SDK calls, and upload and getUploadUrl follow the folder's “create” rule, or else the site's upload gate (signed in as a member of this site). Schedules, webhooks with no sign-in, your own runs and functions with Full data access see every file the site owns.