Files & storage
Every file your workflows, interfaces, and chats touch (images, PDFs, audio, video, exports, uploads) lands in the same storage and is represented the same way everywhere: a single FileRef object. Learn the limits, the shape, and where files show up across the product.
Uploading files
Uploads through the Files page, the Data Input form node, and interface or form file inputs accept up to 100 MB per file. Go over it and the upload is rejected with 413 Payload Too Large.
You can drop a new upload straight into an existing manual folder. An invalid, blank, or cross-workspace folder doesn't fail the upload; the file simply lands at the root of your file browser instead.
Storage quota
A plan's included storage is one pool per account: usage is added up across every workspace the account owns. The quota is checked on every write, and once the pool is full every workspace of that account refuses new files, including one that stored nothing itself.
| Plan | Included storage |
|---|---|
| Free | 100 MB |
| Starter | 1 GB |
| Pro | 10 GB |
| Team | 100 GB |
| Enterprise Basic | 500 GB |
| Enterprise Standard | 1 TB |
| Enterprise Premium | ~2.5 TB |
| Enterprise Ultimate | 5 TB |
Past 80% of the pool the status reads Approaching limit. A write that would go over it fails with 413 and “Storage quota exceeded”. Self-hosted Community Edition on the Free tier has no quota: local storage is unlimited.
The Storage settings page
Settings > Storage shows your usage against the plan, a Storage Breakdown by category (workflows, tables, files), and Recalculate usage to refresh the figures. Choose a single workspace or All workspaces; the account line reads “shared across your workspaces”. Members of a workspace see that workspace's own usage and whether the pool is full, not the account total.
The canonical FileRef
Every file your workflow touches, whatever produced it, is the same shape: a FileRef. It carries a display name, a MIME type, a size in bytes, an id used to build the file's URL, and an internal storage path.
{
"_type": "file",
"id": "b21f6c1e-5a9d-4c1b-9d0e-2f7a8c3e41d2",
"path": "<internal storage key>",
"name": "report.pdf",
"mimeType": "application/pdf",
"size": 48213
}Everything that can hand you a file emits this shape: catalog tools that return binaries (image generation, screenshots, and the like), the file nodes below, interface and form file uploads, and chat attachments. Anything a catalog tool returns as a large base64 blob is also stored and rewritten into a FileRef for you.
To pass a file from one step to the next, map the FileRef object itself, not one of its fields:
{{core:my_download.output.file}}
{{mcp:generate_image.output.images[0]}}Where a FileRef stops being a file
- Tables: store a file in a
fileorimagecolumn and it reads back as a file reference. Stored in atextcolumn it becomes plain text. See Tables & data. - Aggregate: collecting files with an Aggregate step turns each one into text. To gather several files into a list that later steps can still use as files, collect them in a Code step instead.
File nodes
These core nodes produce, transform, or read files. Most of them emit a FileRef under the same output key, .output.file, so once you know the pattern you can wire any of them the same way.
| Node | What it does | Notable outputs |
|---|---|---|
| Download File | Downloads a file from an external URL and stores it. Use it when the source is a public URL (a user pasted an image link into a form, for example). | file, source_url |
| Convert to File | Converts data into CSV, Excel, or JSON and stores the result as a file. | file, result (inline contents), format (default csv), row_count |
| Extract from File | Reads a file. Structured (rows) mode parses CSV, Excel, or JSON into rows. Text (raw content) mode extracts the text of a PDF, Word, HTML, or plain text file and can split it into chunks. | items, format, rowCount; in text mode each item has content, chunk_index, total_chunks |
| Media | Audio and video processing: probe metadata, add an audio track to a video, mix tracks, extract audio, join videos, grab a still frame, overlay an image (watermark), burn in subtitles. | file, duration_seconds; probe returns metadata and no file |
| Public Link | Creates a temporary public URL for a stored file (see below). | url, expires_at, ttl_minutes, file |
| SFTP | Remote file operations: upload, download, list, delete, rename, mkdir. A download emits the FileRef; list returns each remote entry. | files[], file_count, duration_ms |
| Compression | Compresses or decompresses data (gzip, zip). | result, format |
After a catalog tool that already returns a file, you don't need a Download File node too: the file is already stored and already a FileRef. When joining videos with Media, list each input explicitly in the step (up to 8); a list built at run time can't be passed in as a whole.
Chunking documents with Extract from File
In Text (raw content) mode, turn on Enable chunking to split the extracted text into overlapping pieces, for example to embed them into a vector table. Pick a Chunking strategy (Fixed size, Recursive (paragraphs, then sentences), or Custom separator), a Chunk size and an Overlap, counted in characters or tokens. Each chunk becomes one item, ready for a Split.
Giving a file to an external service
Some APIs don't accept an upload and instead fetch the file from a URL you give them. The Public Link node creates a signed URL that anyone can download from, with no sign-in, until it expires:
- Lifetime (
ttl_minutes): 240 minutes (4 hours) by default, from 5 minutes up to 10,080 minutes (7 days). - A link can't be revoked before it expires, so keep the lifetime as short as the receiving service allows.
- It only works on files that belong to your workspace.
Public Link "Share video":
file = {{core:render.output.file}}
ttl_minutes = 60
Later step:
video_url = {{core:share_video.output.url}}Sending a file to an integration tool
To send a stored file into a catalog tool that accepts a file (Telegram's send_photo or send_document, or an image, audio, video, or file parameter on another API), map the FileRef object into that parameter. The platform downloads the bytes for you and sends them in the form the API expects.
Telegram "Send Photo":
photo = {{agent:generate.output.file}}For a multipart form, each field is one of three modes: fileRef (always uploads bytes from a FileRef), param (always sends the raw value as text), or auto (picks automatically: a FileRef becomes a binary upload, a map or list becomes a JSON string, and any plain value, a public URL, a provider file id, a number, is sent as text).
Files in interfaces
Map a FileRef into variable_mapping under any friendly name, then use that name in an <img src>, <a href>, or <video src>. The renderer rewrites the FileRef into a usable URL, for single files and lists of files alike, so there's no URL to build by hand. The page never sees your session token: the app loads each file for it and hands the page the file's contents directly.
<!-- variable_mapping: { "photo": "{{agent:generate.output.file}}" } -->
<img src="{{photo}}" alt="Generated" />A download link needs both the file and its name mapped separately:
variable_mapping:
href: {{core:dl.output.file}}
filename: {{core:dl.output.file.name}}<a href="{{href}}" download="{{filename}}">Download</a>An interface can also collect a file from the user: an <input type="file" name="photo"> inside a form submits through the same contract as a standalone form trigger. The next step reads the uploaded file as a FileRef under {{trigger:<label>.output.photo}}. Form uploads also emit flat sidecar fields (photo_file_url, photo_file_name, photo_file_size, photo_content_type), but prefer the FileRef object in new designs. See Interfaces & apps.
Reading documents & seeing images in chat
When an agent looks at a file, documents are extracted to readable text (so the agent reads the actual content, not just a link) and images are shown to a vision-capable model instead.
| Family | Formats |
|---|---|
| Documents | PDF, Word (.docx), Excel (.xlsx, one line per row), HTML |
| Plain text, code, data | .txt .md .markdown .csv .tsv .json .xml .yaml .yml .log .js .mjs .ts .tsx .jsx .py .java .kt .c .h .cpp .cs .go .rb .rs .php .sh .sql .css .scss .ini .toml .properties .conf .env .srt .vtt (MIME wins, extension is the fallback) |
| Images | Never extracted as text; a vision-capable model sees the raw image directly. |
- Document text extraction is skipped over 10 MB (the file is offered as a link instead, with a note to wire it into an Extract from File node for chunked processing).
- Image vision inlining is skipped over roughly 3.6 MB raw.
- Extracted or read text is paged in 128 KB windows, with an offset to expand further for long files.
The workspace file browser
The file browser shows one tree that mixes folders you create by hand with a virtual folder tree computed automatically from each file's run context: workflow, then epoch (one trigger firing), then run, then item (one iteration of a Split).
Manual folders can be created, and files or folders moved into them or back to the root, but a manual folder can never nest inside a virtual workflow folder, only inside another manual folder or the root.
Browsing is newest-first and paginated. Each entry shows its name, MIME type, size, kind, when it was created, and, when applicable, which run and step produced it. The browser lists real files only; step-output data that isn't a file is read instead through the run and node-output views.
Generating a file
The Generate button in the file browser generates a media asset with an AI model and saves the result as a file. Generated switches the view to the list of assets you generated, with the model and prompt that produced each one; open one to view it, or reuse its settings to generate again. See Studio for the models and options. The buttons appear only where generation is available.
File serving & access control
Every user-facing file link is opaque: it addresses the file by its id, never by the raw storage key (which would otherwise leak the owning workspace's id).
https://your-app/api/proxy/files/by-id/{id}/raw?disposition=inline|attachmentThese links don't expire and are workspace-scoped: any member of the file's workspace can open it. A request from a different workspace gets a plain 404, never a 403, so a link's existence is never leaked to someone who shouldn't have it.
A published marketplace or share preview needs to render for anonymous visitors, so it uses a second, expiring form instead: a signed URL, valid for 4 hours by default, where the signature itself is the authorization.
| Link form | Who it's for | Lifetime |
|---|---|---|
/api/proxy/files/by-id/{id}/raw | Signed-in members of the file's workspace | Does not expire |
/api/files/proxy-signed?...&sig=... | Anonymous marketplace and share previews | 4 hours by default |
| Public Link node URL | Any external service or person you give it to | 4 hours by default, up to 7 days |
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
An upload fails with 413 Payload Too Large | The file is larger than 100 MB. | Split or compress the file before uploading it. |
| A write fails with "Storage quota exceeded" | The storage pool of the account that owns the workspace is full. The pool is shared by all of that account's workspaces. | Delete files you no longer need, then use Recalculate usage in Settings > Storage, or move to a plan with more storage. |
| A file will not attach in the chat composer | On LiveContext Cloud, a chat attachment is limited to 10 MB. | Upload the file on the Files page instead. |
| A later step receives text or an id instead of a file | The step was given .path or .id instead of the whole FileRef, or the file went through a text column or an Aggregate step on the way. | Map the FileRef object itself, store files in file or image columns, and collect several files in a Code step. |
| An upload landed at the root instead of in a folder | The target folder was invalid, blank, or in another workspace. | Move the file into the folder from the file browser. |