Skip to main content
View as Markdown

File Actions

Sixteen operators over storage — moving files, reading their metadata, generating documents, and the closed spreadsheet subset.

File actions operate against the app's configured storage, whether that is the local filesystem or an object store.

Storage

Operator Props Does
upload source, path?, contentType? Uploads a file to storage
download key Downloads a stored file
delete key Deletes a stored file
copy source, destination Copies a stored file
move source, destination Moves or renames a file
list prefix, limit? Lists files under a prefix

Metadata and access

Operator Props Does
getMetadata key Reads size, content type and the rest
signUrl key, expiresIn?, operation? Mints a time-limited URL for download or upload

Generation

Operator Props Does
generatePdf template, filename, data?, pageSize?, orientation?, margins?, destination? Renders an HTML template to PDF
generateCsv data, filename, columns?, delimiter?, includeHeaders?, destination? Writes a CSV from an array
generateXlsx data? or sheets?, filename, columns?, sheetName?, destination? Writes a workbook from rows

Parsing and transforming

Operator Props Does
parseCsv source?, key?, content?, columns?, skipRows?, delimiter? Parses CSV into rows
parseXlsx source?, key?, sheet?, header?, range?, skipRows? Parses a worksheet into rows
extractText source, format? Extracts text from a document
transformImage source, width?, height?, fit?, format?, quality?, destination? Resizes or converts an image
compress files, filename, destination? Zips several files into one archive
Path Kind Values Default Description
name string Step name for referencing outputs (e.g., "fetchUser"). Must be alphanumeric + underscore.
label string Human-readable label for this action step
continueOnError boolean false Continue workflow even if this action fails (default: false)
timeout number Per-action timeout in ms (1000-900000). Terminates the action when exceeded.
type enum file Constant value 'file' for type discrimination in discriminated unions
operator enum upload, download, delete, copy, move, list, getMetadata, signUrl, generateCsv, generatePdf, parseCsv, extractText, transformImage, compress, parseXlsx, generateXlsx Selects the operation within the 'file' action family; it decides which props the step takes

retry

Path Kind Values Default Description
retry object Automatic retry behavior for failed executions with fixed or exponential backoff
retry.maxAttempts number Maximum retry attempts (1-10)
retry.delayMs number Base delay between retries in milliseconds (100-60000, default: 1000)
retry.strategy enum fixed, exponential Retry strategy: fixed delay or exponential backoff (default: fixed)

props

Path Kind Values Default Description
props object
props.source string
props.path string Storage key destination. If omitted, auto-generated.
props.contentType string
props.key string
props.sourceKey string
props.destinationKey string
props.prefix string Storage key prefix to list files under
props.limit number Maximum number of files to return
props.expiresIn number URL expiration time in seconds (default: 3600)
props.operation enum download, upload, resize, convert
props.data string | object
props.filename string
props.columns array
props.columns[] object
props.columns[].key string
props.columns[].field string Object key to extract as column value
props.columns[].header string
props.delimiter enum ,, ;, , |
props.includeHeaders boolean Include column headers as first row (default: true)
props.destination string Storage key for the output file. If omitted, file is stored in temporary storage and auto-cleaned after STORAGE_TEMP_CLEANUP_AFTER (default: 24 hours).
props.template string HTML template for PDF content (supports template variables)
props.pageSize enum A4, A3, Letter, Legal Page size (default: A4)
props.orientation enum portrait, landscape Page orientation (default: portrait)
props.margins object Page margins
props.margins.top string Top margin (e.g., "1cm", "0.5in")
props.margins.right string Right margin
props.margins.bottom string Bottom margin
props.margins.left string Left margin
props.content string Inline CSV text to parse (alternative to source/key)
props.columns[].name string Object key for the parsed value
props.columns[].index number Zero-based CSV column index to read this value from
props.skipRows number
props.format enum plain, markdown, jpeg, png, webp
props.width number Target width in pixels (1-2500)
props.height number Target height in pixels (1-2500)
props.fit enum fill, inside How a two-dimension resize uses the box: fill stretches to exactly width x height (distorts); inside scales down to fit within the box, preserving the aspect ratio (output may be smaller than requested). Required when both width and height are set; ignored otherwise.
props.outputFormat enum jpeg, png, webp Output image format (operation: convert)
props.quality number Output quality for lossy formats (1-100, default: 80)
props.keys array Array of storage keys to compress into the archive
props.keys[] string String with {{step.property}} variables, {{helper args}} expressions, and $env.VAR references
props.files string Template resolving to array of storage keys to compress
props.sheet string | number Sheet to read: a sheet name, or a zero-based sheet index (default: first sheet)
props.header boolean Treat the first row as a header row — it becomes columns and is excluded from data (default: false)
props.range string A1-style range to read, e.g. "A1:C10" (default: the sheet's full used range)
props.sheets array Multi-sheet output, one entry per worksheet (alternative to data)
props.sheets[] object
props.sheets[].name string Worksheet name as it appears on the tab
props.sheets[].data string Template variable referencing this sheet's rows
props.sheets[].columns array (truncated) Column definitions for this sheet
props.sheetName string Worksheet name for the single sheet (default: "Sheet1")

CSV

parseCsv needs exactly one input: source (a storage key), key (its alias), or content — inline CSV text, which parses a webhook body or an earlier step's output without a storage round-trip. Omitting all three is a configuration error.

skipRows drops that many leading non-blank lines and nothing else, so it strips a preamble without changing the shape of the output: the first line that survives is still read as the header, and rows stay keyed by header name.

delimiter is one of comma, semicolon, tab or pipe. Omitted, it is auto-detected by counting candidates outside quoted fields in the first surviving line, so a semicolon-delimited export whose header legitimately contains a comma still reads correctly. Pass columns to map explicitly instead, each entry taking a name plus either a header name or a zero-based index.

Spreadsheets — a closed subset

parseXlsx and generateXlsx support a named, closed subset of the spreadsheet format rather than the format at large. On the way in that subset is: shared and inline strings, numbers, booleans, dates recognised through the cell's number format — an Excel date serial is otherwise indistinguishable from a plain number — and formulas read as their cached value, never evaluated. Dates come back as ISO 8601 strings rather than date objects, so they survive being persisted to run history and re-read by a template.

The wall is at data outside the cell grid

A workbook carrying a chart, a drawing, an embedded image, a pivot table or a macro is refused by name. Reading it would hand back the cells and quietly drop the part of the document its author cared about, which is the worst available outcome: a plausible answer that is missing the point of the file.

Refusal is detected on both the archive's part paths and its content-type overrides, because the two can disagree. Cosmetic styling — fonts, fills, borders — is not refused but ignored, since refusing it would refuse essentially every real workbook. Outside the subset the action fails loudly and deliberately: convert the file rather than trust a wrong answer.

Reading

parseXlsx takes the workbook as source or its alias key; a data URI and an https URL are also accepted. sheet selects a worksheet by name, or by zero-based position when given a number; omitted, the first sheet in the workbook's declared order is read. range restricts reading to an A1-style window, defaulting to the sheet's used range. header treats the first row as a header, promoting it to columns and excluding it from the data. skipRows then drops that many rows off the top of the grid.

The output carries the rows plus the sheet's name, every sheet name in the workbook, the row count and the columns. That list of names is what lets an automation discover a workbook's sheets on a first call and target one on a second.

app.yaml
- name: importSheet
  type: file
  operator: parseXlsx
  props:
    source: '{{trigger.data.key}}'
    sheet: Orders
    header: true
    range: 'A1:F500'

Writing

generateXlsx writes the minimal part set a consumer requires, adding a styles part only when a date cell is present. Pass data for a single sheet, naming it with sheetName, or sheets for several, each entry taking a name, its own data and optionally its own columns. columns selects and orders the fields and supplies their header labels.

The refusal here is per cell value: anything outside string, number, boolean and date — an object, an array, a bigint, a not-a-number, an invalid date — fails the step with an error naming the sheet and the cell reference, rather than being coerced into a plausible-looking string.

Round-tripping a generated workbook back through the parser is lossless within the subset, and only within it.

app.yaml
- name: exportOrders
  type: file
  operator: generateXlsx
  props:
    data: '{{fetchOrders.records}}'
    filename: 'orders-{{trigger.data.month}}.xlsx'
    sheetName: Orders
    destination: exports/

PDF and images

app.yaml
- name: invoice
  type: file
  operator: generatePdf
  props:
    template: invoice-template
    filename: 'invoice-{{trigger.data.id}}.pdf'
    data: '{{trigger.data}}'
    pageSize: A4
    orientation: portrait
    destination: invoices/
app.yaml
- name: thumbnail
  type: file
  operator: transformImage
  props:
    source: '{{upload.result.key}}'
    width: 320
    format: webp
    quality: 70

A conversion naming no format encodes to WebP, and a plain resize keeps the source format rather than transcoding it. That is the built-in behaviour rather than a setting — name a format on the action when a particular consumer needs another codec.

Behaviour

Compress Action

  • Creates ZIP from array of storage keys
  • With destination stores ZIP at specified key
  • Without destination uses temp storage
  • Result includes fileCount
  • Nonexistent source key fails gracefully

Copy Action

  • Copy creates duplicate at destination key
  • Copy preserves original at source key
  • Copy to existing key overwrites destination

Delete Action

  • Delete by key removes file and metadata
  • Delete nonexistent key returns error (respects continueOnError)

Download Action

  • Download by key returns metadata and stores content in temp key
  • Download nonexistent key returns error
  • Download result usable as source in subsequent file action

Extract Text Action

  • Extract from PDF returns plain text
  • Markdown format returns structured markdown
  • Result includes wordCount and pageCount
  • Unsupported format returns error

Generate CSV Action

  • File generateCsv creates CSV from data array with configurable column mapping
  • GenerateCsv with destination stores file at specified storage key
  • GenerateCsv without destination uses temp storage with auto-cleanup
  • GenerateCsv result contains key, filename, contentType, size
  • GenerateCsv with delimiter and includeHeaders=false
  • GenerateCsv result key is usable as source in subsequent action
  • A value containing the configured delimiter is quoted on disk, not split
  • Delimiter-bearing output round-trips: parseCsv reads back the original value

Generate PDF Action

  • File generatePdf creates PDF from HTML template with template variable substitution
  • GeneratePdf with destination stores file at specified storage key
  • GeneratePdf without destination uses temp storage with auto-cleanup
  • GeneratePdf result contains key, filename, contentType, size
  • GeneratePdf with pageSize, orientation, and margins
  • GeneratePdf result key is usable as source in subsequent action

Generate XLSX Action

  • Writes a real OOXML package to destination — a ZIP container carrying the parts an .xlsx consumer requires
  • Values survive a generateXlsxparseXlsx round-trip with their types intact, not flattened to strings
  • Honours an explicit columns selection and its header labels — an undeclared key never becomes a cell
  • The sheets[] form emits every declared worksheet, under its declared name, in declaration order

Get Metadata Action

  • Returns file info without downloading content
  • Nonexistent key returns error

List Action

  • List by prefix returns matching file metadata array
  • List with limit restricts result count
  • List empty prefix returns empty array

Move Action

  • Move relocates to destination and deletes source
  • Move result contains new key
  • Source key no longer exists after move

Parse CSV Action

  • Reads CSV from storage key, returns structured data array
  • Custom columns maps CSV columns to object keys
  • skipRows skips header/metadata rows
  • Custom delimiter parses semicolon/tab files
  • Result data usable in subsequent record batchCreate
  • A newline inside a quoted field stays one row (RFC 4180)
  • Quoted whitespace is preserved; unquoted cells still trim
  • A semicolon file whose header contains one stray comma is detected as semicolon-delimited
  • Inline content parses with no storage round-trip
  • A data: URI source parses with no storage round-trip
  • An https:// source is fetched over the network, then parsed
  • A windows-1252 export decodes its accented characters
  • skipRows drops preamble lines and still yields header-keyed rows

Parse XLSX Action

  • Reads a workbook from a storage key, typing each cell as a string, a number or a boolean rather than as text throughout
  • Resolves cells written as inline strings, not only those pointing into the shared-string table
  • A serial carrying a date number format reads as a date, while the same bare serial stays a number
  • A formula cell yields the workbook's cached value — the reader never re-evaluates the formula
  • sheet given a name reads that worksheet rather than the first
  • sheet given a zero-based integer selects positionally
  • Reports every sheet name in workbook order, so a follow-up action can target one it discovered
  • Refuses a workbook outside the supported OOXML subset by name, instead of misparsing it
  • Refuses a file that is not a ZIP container at all

Sign URL Action

  • Generates time-limited download URL
  • Respects custom expiresIn
  • Upload operation generates upload-capable URL

Temp Storage Cleanup

  • A temp file older than STORAGE_TEMP_CLEANUP_AFTER is removed by the next temp write, while that write's own file survives
  • The sweep never removes files outside tmp/automations/, regardless of age
  • STORAGE_TEMP_CLEANUP_AFTER=0 disables sweeping entirely

Transform Image Action

  • Resize creates resized image at destination
  • A crop configuration is refused at validation
  • Convert changes image format (png to webp)
  • Quality setting controls output compression
  • Without destination uses temp storage
  • A two-dimension resize must name its fit
  • An avif output format is refused at validation

Upload Action

  • Upload from base64 data with auto-detected contentType
  • Upload from URL source to storage path
  • Upload from previous step result key
  • Upload to temp storage when no path specified

Last updated September 23, 2026

This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.

Built with Sovrium