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.
| Feature | Description |
|---|---|
| Creator tracking | Each upload records who uploaded the file. Use creator rules to restrict access. |
| Password protection | Optionally require a password to download a file. |
| Expiry dates | Set a date after which the file can no longer be accessed. |
| Download restriction | Allow viewing only (inline), preventing downloads. |
File Metadata
Each file stores metadata automatically:
| Field | Description |
|---|---|
| id | Unique file identifier |
| name | Original file name |
| mimeType | File type (e.g., image/png, application/pdf) |
| size | File size in bytes |
| createdAt | Upload timestamp |
| creatorId | ID 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.