Skip to content

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:

"kvHJ4ipZ3Q6EAoguKrWmU7KYyDHcU03C"

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

GET /storage/ws/{workspace}/api/v1/render/{render_id}

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:

{
  "invoiceNumber": "INV-001",
  "pdfFile": "kvHJ4ipZ3Q6EAoguKrWmU7KYyDHcU03C"
}

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: true for 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

Limits

  • Max file size: 500 MB per upload
  • Upload rate: 100 requests/minute