Storage¶
Overview¶
Centrali Storage holds files that live alongside your records, functions, and application workflows. Use it for uploads, generated exports, media assets, and attachments that your app needs to serve later.
The storage model has two identifiers:
- Absolute paths under
/root/...for organizing, listing, and deleting files - Render IDs for delivering file contents through render and download URLs
If you keep those two roles separate, the rest of the API is much easier to reason about.
Key Features¶
- Private by default: Uploaded files require authenticated access unless you explicitly mark them public
- Large file support: Upload files up to 500 MB
- Audio & video: Upload and stream media files with seeking support
- Path-based organization: Manage files through folders under
/root/ - Integration: Reference files from records
- Streaming delivery: Render and download endpoints support efficient file serving
File Access Model¶
Storage is one of the higher-trust surfaces in the platform because it is easy to confuse "file exists in my workspace" with "file is safe to expose to a browser". The important rules are:
| Behavior | What it means |
|---|---|
isPublic: false (default) | File delivery requires an authenticated request |
isPublic: true | Anyone who knows the render or download URL can access the file |
| Folder path | Used for file management and organization |
| Render ID | Used for delivery URLs and file references in application data |
Public means public
If you upload a file with isPublic: true, the render and download URLs become public delivery URLs. Do not use this for invoices, exports, private media, or anything user-specific unless public access is intentional.
For private files, use an authenticated client flow such as a service account, BYOT user token, or scoped frontend access through the SDK. For auth setup patterns, see SDK Authentication and Authentication Overview.
Folder Structure¶
Every workspace has a predefined folder structure. Files must be uploaded to folders within this structure.
Root Folders¶
| Folder | Purpose | User Access |
|---|---|---|
/root | User files and data | Full access |
/root/shared | Default upload location | Full access |
/system | System files (avatars, etc.) | Read-only |
/support | Support and diagnostic files | Read-only |
Default Upload Location¶
If you don't specify a location when uploading, files are automatically placed in /root/shared.
Path Format¶
All paths must be absolute, starting with /root/. For example: - /root/shared - Default shared folder - /root/shared/images - Images subfolder - /root/shared/documents/invoices - Nested folder structure
Use paths when you are deciding where a file lives. Use render IDs when you are deciding how a file is served later.
Creating Folders¶
Create folders using the API before uploading files to them:
# Create a folder in /root/shared
curl -X POST https://api.centrali.io/storage/ws/my-workspace/api/v1/folders \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "images",
"parentPath": "/root/shared"
}'
Create a subfolder inside an existing folder:
# Create a subfolder using parent folder ID
curl -X POST https://api.centrali.io/storage/ws/my-workspace/api/v1/folders/{parent_folder_id}/sub-folders \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "thumbnails"
}'
Listing Folders¶
curl -X GET https://api.centrali.io/storage/ws/my-workspace/api/v1/folders \
-H "Authorization: Bearer YOUR_TOKEN"
Uploading Files¶
Upload files to a specific folder or let them default to /root/shared:
# Upload to default location (/root/shared)
curl -X POST https://api.centrali.io/storage/ws/my-workspace/api/v1/storage/upload \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@/path/to/file.pdf" \
-F "fileName=my-document.pdf"
# Upload to a specific folder (must exist)
curl -X POST https://api.centrali.io/storage/ws/my-workspace/api/v1/storage/upload \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "file=@/path/to/file.pdf" \
-F "fileName=my-document.pdf" \
-F "location=/root/shared/documents"
Response:
The response is a render ID - a unique identifier for the uploaded file's delivery surface. Use this ID to construct render or download URLs and to store a reference in your records.
Constructing File URLs¶
Use the render ID to access your file:
| Action | URL Pattern |
|---|---|
| Render (display inline) | /storage/ws/{workspace}/api/v1/render/{renderId} |
| Download (as attachment) | /storage/ws/{workspace}/api/v1/download/{renderId} |
Example with authenticated access:
# Render the file (e.g., display image in browser)
curl "https://api.centrali.io/storage/ws/my-workspace/api/v1/render/kvHJ4ipZ3Q6EAoguKrWmU7KYyDHcU03C" \
-H "Authorization: Bearer YOUR_TOKEN"
# Download the file as attachment
curl "https://api.centrali.io/storage/ws/my-workspace/api/v1/download/kvHJ4ipZ3Q6EAoguKrWmU7KYyDHcU03C" \
-H "Authorization: Bearer YOUR_TOKEN" \
--output file.pdf
If using the SDK, use the helper methods:
const renderId = result.data;
const renderUrl = client.getFileRenderUrl(renderId);
const downloadUrl = client.getFileDownloadUrl(renderId);
If the file is private, the URL still needs an authenticated request path when the browser fetches it. Bare render/download URLs are only appropriate for intentionally public files or clients that already provide the required access mechanism.
Upload Parameters¶
| Parameter | Required | Default | Description |
|---|---|---|---|
file | Yes | - | The file to upload |
fileName | Yes | - | Name for the stored file |
location | No | /root/shared | Target folder path (must exist) |
isPublic | No | false | If true, render and download URLs become publicly accessible without auth |
Downloading Files¶
Download files using the render ID:
curl -X GET "https://api.centrali.io/storage/ws/my-workspace/api/v1/download/{renderId}" \
-H "Authorization: Bearer YOUR_TOKEN" \
--output file.pdf
download/{renderId} serves the same stored file as render/{renderId}, but with attachment-style download behavior instead of inline rendering.
Image Resize & Compression¶
Centrali supports on-the-fly image transformation when rendering images. Resize, compress, and convert image formats without storing multiple versions.
Render Endpoint¶
Query Parameters¶
| Parameter | Type | Default | Description |
|---|---|---|---|
width | int | - | Target width in pixels (max 4096) |
height | int | - | Target height in pixels (max 4096) |
quality | int | 85 | JPEG quality 1-100 (only for JPEG output) |
format | string | original | Output format: jpeg, png, or webp |
Examples¶
Resize to specific width (maintains aspect ratio):
curl "https://api.centrali.io/storage/ws/my-workspace/api/v1/render/abc123?width=200" \
-H "Authorization: Bearer YOUR_TOKEN"
Resize to fit within dimensions:
curl "https://api.centrali.io/storage/ws/my-workspace/api/v1/render/abc123?width=800&height=600" \
-H "Authorization: Bearer YOUR_TOKEN"
Compress JPEG with lower quality:
curl "https://api.centrali.io/storage/ws/my-workspace/api/v1/render/abc123?format=jpeg&quality=60" \
-H "Authorization: Bearer YOUR_TOKEN"
Convert PNG to JPEG:
curl "https://api.centrali.io/storage/ws/my-workspace/api/v1/render/abc123?format=jpeg" \
-H "Authorization: Bearer YOUR_TOKEN"
Supported Image Formats¶
- JPEG / JPG
- PNG
- WebP
- GIF (converted to PNG on output)
- BMP
- TIFF
Behavior¶
- Aspect ratio: Automatically preserved when only width or height is specified
- Both dimensions: Image fits within the specified bounds while maintaining aspect ratio
- Quality: Only applies to JPEG output format
- Fallback: Returns original image if transformation fails
- Non-images: Files that aren't images are returned unchanged
Use Cases¶
- Thumbnails: Generate thumbnails on-the-fly for galleries
- Responsive images: Serve appropriately sized images for different devices
- Bandwidth optimization: Compress images for faster loading
- Format conversion: Convert to more efficient formats like WebP
Audio & Video¶
Upload audio and video files just like any other file. The storage service supports HTTP range requests, so media files can play progressively and seek without downloading the entire object first.
Playback¶
Use the render URL with standard HTML5 elements:
<!-- Audio playback -->
<audio src="https://api.centrali.io/storage/ws/my-workspace/api/v1/render/{renderId}" controls></audio>
<!-- Video playback -->
<video src="https://api.centrali.io/storage/ws/my-workspace/api/v1/render/{renderId}" controls></video>
These examples assume the file is public or your frontend already has a supported way to make the file request with the needed access context.
With the SDK:
const renderId = result.data;
const url = client.getFileRenderUrl(renderId);
// Use in your frontend
<audio src={url} controls />
<video src={url} controls />
Range Requests¶
The download and render endpoints include Accept-Ranges: bytes in responses. Clients can request specific byte ranges:
# Request first 1 MB of a video
curl "https://api.centrali.io/storage/ws/my-workspace/api/v1/render/{renderId}" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Range: bytes=0-1048575"
# Response: HTTP 206 Partial Content
# Content-Range: bytes 0-1048575/52428800
Browsers use this automatically for <audio> and <video> seeking when the delivery URL is reachable from the client.
Supported Formats¶
Any audio or video format can be uploaded and stored. Browser playback depends on the format — for broadest compatibility, use:
- Video: MP4 (H.264/AAC)
- Audio: MP3 or AAC
Console Preview¶
The console storage panel shows an inline audio/video player when you click on a media file, letting you preview content without downloading.
Listing Files¶
curl -X GET "https://api.centrali.io/storage/ws/my-workspace/api/v1/storage/list?prefix=/root/shared/documents/" \
-H "Authorization: Bearer YOUR_TOKEN"
Use the same absolute /root/... path convention for listing that you use for folder creation and uploads.
Deleting Files¶
curl -X DELETE "https://api.centrali.io/storage/ws/my-workspace/api/v1/storage/%2Froot%2Fshared%2Fdocuments%2Ffile.pdf" \
-H "Authorization: Bearer YOUR_TOKEN"
Delete uses the file path, not the render ID. URL-encode the absolute path when you place it in the request URL.
Referencing Files in Records¶
Store file references in record properties:
{
"name": "invoice",
"properties": [
{ "name": "invoiceNumber", "type": "string" },
{ "name": "pdfFile", "type": "string" }
]
}
Create record with file reference:
Prefer storing the render ID in records when the application later needs to render or download the file. If you also care about folder location or original file name, store that as separate metadata rather than overloading one field.
Using Storage in Functions¶
Functions can create and store files using the api.storeFile() method. This is useful for generating reports, exporting data, or processing files programmatically.
async function run() {
// Generate a CSV report
const csvContent = "name,email,status\nJohn,john@example.com,active";
// Store the file
const result = await api.storeFile(
csvContent,
"daily-report.csv",
{
mimeType: "text/csv",
encoding: "utf8", // Use "base64" for binary files (PDFs, images)
folder: "/root/shared/reports", // Optional: target folder (must exist)
isPublic: false // Keep generated exports private unless public delivery is intentional
}
);
if (result.success) {
api.log(`File stored successfully`);
api.log(`URL: ${result.fileUrl}`);
api.log(`Render ID: ${result.renderId}`);
// Save the renderId to a record for future reference
await api.updateRecord(executionParams.recordId, {
reportFile: result.renderId
});
} else {
api.logError(`Failed to store file: ${result.error}`);
}
return { success: result.success };
}
File Storage Options¶
| Option | Type | Default | Description |
|---|---|---|---|
mimeType | string | (required) | MIME type (e.g., "application/pdf", "text/csv") |
encoding | string | "utf8" | Content encoding: "utf8" for text, "base64" for binary |
folder | string | "/root/shared" | Target folder path |
isPublic | boolean | false | If true, the file's render and download URLs are accessible without authentication |
Limits¶
| Limit | Value |
|---|---|
| Max file size (compute) | 500 MB |
| Supported encodings | utf8, base64 |
For complete documentation including examples for PDFs, images, and error handling, see the Writing Functions Guide.
Best Practices¶
- Use full paths: Always use absolute paths starting with
/root/(e.g.,/root/shared/documents) - Create folders first: Create folders via API before uploading files to them
- Organize by type: Use subfolders to organize files (e.g.,
/root/shared/images,/root/shared/documents) - Store render IDs in records: Use render IDs for delivery references; keep path or filename as separate metadata if you need them
- Keep sensitive files private: Only set
isPublic: truefor assets you are comfortable serving to anyone with the URL - Use safe file names: Avoid spaces and special characters in file names
- Clean up regularly: Delete unused files to manage storage quota
- Don't use system folders: Only upload to
/root/paths;/system/and/support/are reserved
What to Read Next¶
- SDK Authentication for frontend and backend access patterns
- Functions and Writing Functions for server-side file generation
- Limits & Quotas for file size and workspace quota limits
Limits¶
- Max file size: 500 MB per upload
- Upload rate: 100 requests/minute