Skip to content

Working with Files

QuickFlo provides a full set of file operations for reading, writing, downloading, and managing files in your workflows. Every file step supports both managed storage (zero-config, hosted by QuickFlo) and bring-your-own cloud storage providers.

Step library showing all file steps and an example workflow chaining download, write, read, and get-signed-url steps
StepWhat it does
file.writeWrite content to a file in cloud storage
file.readRead a file’s content from cloud storage
file.download-from-urlDownload a file from an HTTP(S) URL
file.deleteDelete a file from cloud storage
file.copyCopy a file to a new path
file.moveMove a file to a new path
file.listList files in a storage directory
file.existsCheck whether a file exists at a given path
file.get-signed-urlGenerate a temporary signed URL for secure file access
file.generate-pdfRender an HTML string into a PDF file written to cloud storage

Every file step that interacts with cloud storage includes a Storage section with four tabs:

The default option. Files are stored in QuickFlo’s hosted storage, scoped to your organization. No configuration needed — just provide a file path and QuickFlo handles the rest.

Managed storage paths are relative to your organization’s namespace. For example, writing to reports/monthly.csv stores the file at an organization-scoped path that only your workflows can access.

Connect your own GCS bucket. Provide a service account key and reference files using gs:// URLs (e.g., gs://my-bucket/data/file.csv).

Connect any S3-compatible storage — AWS S3, Cloudflare R2, MinIO, and others. Provide an access key, secret key, region, and bucket. Reference files using s3:// URLs.

Connect to an SFTP server for file operations. Provide host, port, username, and authentication credentials. Reference files using sftp:// URLs.

The file.write step saves content to cloud storage.

File Write step editor showing File Path, Content, Content Type, Base64 Decode, and Storage provider tabs
FieldDescription
File PathWhere to store the file (e.g., data/output/report.csv)
ContentThe file content — use templates to pass data from previous steps
Content TypeMIME type of the file (e.g., text/csv, application/json)
Base64 DecodeEnable when the content is base64-encoded (e.g., binary files from an API response)

The file.read step retrieves a file’s content from cloud storage.

File Read step editor showing File URL field, Return as Base64 option, and Storage Credentials tabs
FieldDescription
File URLThe storage URL of the file — use the url from a previous write step’s output (e.g., {{ write-step.file.url }})
Return as Base64Return content as base64 instead of UTF-8 text — use this for binary files like images or PDFs

The file.download-from-url step fetches a file from any public HTTP(S) URL and returns it as a file object you can pass to other steps.

Download from URL step editor showing URL, filename override, MIME type, timeout, and request headers
FieldDescription
Download URLThe HTTP(S) URL to download from
File NameOverride the auto-detected filename
MIME TypeOverride the auto-detected content type
TimeoutMax wait time in milliseconds (default: 30,000)
Request HeadersCustom headers to send with the download request (e.g., auth tokens)
Return as Base64Return the downloaded content as base64

The file.get-signed-url step creates a temporary, pre-authenticated URL for accessing a file without exposing your storage credentials.

Get Signed URL step editor and output showing signedUrl, originalUrl, and expiresAt fields
FieldDescription
File URLThe storage URL of the file
Expires InHow long the signed URL remains valid (in minutes, default: 60)

Output:

{{ get-signed-url-step.signedUrl }} // Temporary public URL
{{ get-signed-url-step.originalUrl }} // Original storage URL (gs://, s3://, etc.)
{{ get-signed-url-step.expiresAt }} // Expiration timestamp

The file.generate-pdf step renders HTML into a PDF and writes it to cloud storage. Useful for invoices, reports, exports, or any “give me back a PDF” workflow.

FieldDescription
SourceWhere the HTML comes from — inline (paste HTML directly), stored-file (HTML file already in storage), or public-url (fetch HTML from a URL)
FilenameName of the resulting PDF (default: document.pdf)
CSSOptional custom CSS string applied on top of the document
Include Base StylesIf true (default), QuickFlo applies a baseline stylesheet so unstyled HTML still renders cleanly
Page SizeA4 (default), Letter, Legal, A3, A5, or Tabloid
LandscapeIf true, render in landscape orientation (default false)
PDF FeaturesOptional add-ons: page numbers (with position and format), bookmarks (default on), a running header, and document info (title, author, subject)
StorageSame provider tabs as the other file steps (managed, GCS, S3, SFTP)

The output is a standard file object pointing at the generated PDF (URL only — the binary content isn’t returned in workflow context to avoid bloat) plus a pageCount field. Pipe the file into file.get-signed-url to return a download link from a webhook trigger, or attach it to an email.

Steps that produce files (download-from-url, read, write, generate-pdf) return a file object on success or a fileError object on failure. You can branch on which one is present.

Workflow execution output showing a file object with filename, type, size, and source URL
{{ download-step.file.filename }} // e.g., "report.csv"
{{ download-step.file.content }} // File content (text or base64)
{{ download-step.file.mimeType }} // e.g., "text/csv"
{{ download-step.file.size }} // File size in bytes
{{ download-step.file.url }} // Source URL

When a file operation fails (missing credentials, file not found, network failure, etc.) the step returns a fileError object instead of file. The step still completes successfully from the workflow’s perspective, so you can detect and handle the error inline:

{{ read-step.fileError.message }} // Human-readable error message
{{ read-step.fileError.code }} // Taxonomy error code (see table below)

fileError.code is one of a stable set of QuickFlo taxonomy codes — extracted from whatever the underlying storage SDK threw and mapped to a vocabulary you can branch on:

CodeCauseMeaning
FILE_NOT_FOUNDpermanentFile or path does not exist
FILE_ACCESS_DENIEDpermanentStorage backend rejected the operation (ACL / IAM)
INVALID_CREDENTIALSpermanentStorage credentials invalid, expired, or missing scopes
BUCKET_NOT_FOUNDpermanentTarget bucket / container does not exist
QUOTA_EXCEEDEDpermanentStorage account quota or plan limit reached
INVALID_PATHpermanentPath is invalid for this backend (illegal chars, too long)
STORAGE_TIMEOUTtransientTimed out talking to the backend
STORAGE_CONNECTION_FAILEDtransientCould not reach the backend (DNS / connection reset)
STORAGE_RATE_LIMITEDtransientBackend throttled the request (S3 SlowDown, GCS 429)
STORAGE_TEMPORARILY_UNAVAILABLEtransientBackend returned a 5xx
STORAGE_UNKNOWN_ERRORunknownUnclassified — retried by default

The same codes drive the engine’s retry decisions — see Error Handling → Which step types have taxonomies.

Branch on which one is present:

{% if read-step.fileError %}
Skipping — {{ read-step.fileError.message }}
{% else %}
Got file: {{ read-step.file.filename }}
{% endif %}

Download a file from an external API, then write it to managed storage:

Step 1 (file.download-from-url): Download from https://api.example.com/export.csv
Step 2 (file.write): Path = exports/{{ initial.date }}.csv
Content = {{ download-step.file.content }}

Write a file to storage, then create a signed URL to return to the caller:

Step 1 (file.write): Path = reports/{{ initial.reportId }}.pdf
Step 2 (file.get-signed-url): File URL = {{ write-step.file.url }}
Step 3 (return): Body = { "downloadUrl": "{{ get-signed-url-step.signedUrl }}" }

Read a file, process its content with a data step, then write the result back:

Step 1 (file.read): File URL = gs://my-bucket/raw/data.json
Step 2 (data.map): Transform the parsed content
Step 3 (file.write): Path = processed/data.json, Content = {{ map-step.output }}