FTP / SFTP
List, download, upload, move and delete files on a remote file server.
An FTP / SFTP node (ftp) moves files between a run and a remote file server — an ERP export to pick
up, a supplier drop to read, a scanned document to deliver. Three protocols: SFTP (file
transfer over SSH), FTPS (FTP over TLS) and plain FTP.
Configuration
| Field | Type | Default | Description |
|---|---|---|---|
protocol | sftp | ftps | ftp | sftp | SFTP if the server offers it. |
host | string | '' | The server. |
port | integer 0–65535 | 0 | 0 uses the protocol's default (22, 990, 21). For FTPS, 21 means explicit TLS. |
username | string | '' | The login. |
password | {{secret.KEY}} reference | — | Only a secret reference. |
privateKey | {{secret.KEY}} reference | — | SFTP key authentication. |
passphrase | {{secret.KEY}} reference | — | The private key's passphrase. |
hostKeyFingerprint | string | '' | SFTP: the server's expected SHA256:… host-key fingerprint. Required. |
operation | list | download | upload | rename | delete | list | rename is Move. |
path | string | '' | The folder or file to act on. Expressions allowed. |
recursive | boolean | false | List: include subfolders. |
uploadSource | file | text | file | Upload: a file from the run, or text you write. |
uploadFileExpression | string | '' | Upload: the file, such as {{ upstream.<nodeId>.output.file }}. |
uploadText | string | '' | Upload: the text. |
newPath | string | '' | Move: the destination. |
createDirectories | boolean | false | Upload / Move: create missing folders on the way. |
overwrite | boolean | false | Move: replace a destination that is already there. Off, an existing destination stops the node. |
deleteFolder | boolean | false | Delete: the path is a folder. |
deleteRecursive | boolean | false | Delete: also delete everything inside it. |
connectTimeoutSeconds | integer, up to 120 | 20 | Bounds reaching the server. |
transferTimeoutSeconds | integer, up to 900 | 120 | Bounds one transfer or listing. |
Pick SFTP if the server offers it. Plain FTP sends the password and every byte of every file in clear over the network, and the panel says so when you choose it.
The connection lives in the node, like Send Email's SMTP settings: the server, its port, the
username and a credential. The credential is always a reference to a secret —
{{secret.FTP_PASSWORD}} — held under Settings → Secrets. A password typed into the node is
refused by the save itself, because a credential written into a workflow is written into every
saved version of it, cannot be rotated and cannot be audited.
Ports. Leave the port at 0 for the protocol's default — 22 for SFTP, 21 for FTP, 990 for
FTPS. For FTPS the port also decides the TLS mode: 990 (and anything other than 21) is implicit
TLS, encrypted from the first byte, while 21 is explicit TLS (AUTH TLS). That is the one
thing about FTPS worth checking against your server's documentation, and the panel tells you which
one it is about to use.
SFTP: the host key
SFTP asks for one more thing: the expected host-key fingerprint of your server. Run
ssh-keyscan your-server.example | ssh-keygen -lf -and paste what it prints (SHA256:…). A server presenting a different key is refused, and the
error names the key it did present so you can compare it yourself.
There is no "trust it the first time" option, and there will not be one. Trusting the first connection means trusting a connection nobody was watching — a workflow runs unattended, so the one connection an attacker would most want to intercept is exactly the one that would be accepted and then remembered. If your server is rebuilt or its key rotated, check the new fingerprint against the server and update the node.
Output
Every operation returns:
| Field | Type | Description |
|---|---|---|
protocol | string | ftp, ftps or sftp. |
host | string | The server. |
operation | string | The operation that ran. |
path | string | The path it ran on. |
affectedPaths | string[] | Every remote path the step touched. |
and, per operation, the fields below.
What each operation does
| Operation | What you set | What comes back |
|---|---|---|
| List | a folder, and whether to include subfolders | entries (name, path, type, size, modified date) and count |
| Download | a file path | file — an ordinary file reference, and size |
| Upload | a destination path, and either a file from the run or text you write | size |
| Move | a source and a destination | oldPath and newPath |
| Delete | a path, and whether it is a folder | deleted |
A Download's file is the same reference every other file in the product uses, so
{{ upstream.<nodeId>.output.file }} can be attached to an AI Prompt, sent as an email
attachment, or passed to an HTTP Request — or uploaded to a different server by a second FTP node.
An Upload can send a Convert to File output, an HTTP Request file response, a Form Trigger upload or another FTP Download — or text you write, with expressions filled in first.
entries[].path is the full remote path, so a List can feed a Loop whose body downloads each file
without rebuilding the path.
modifiedAt can be null, and that is honest rather than missing. Plain FTP's directory
listing format was never standardised, so a date read out of it cannot be relied on for a
comparison; where that is the case the node reports null rather than a guess. SFTP always has a
real date.
Moving and deleting safely
Move does not replace an existing file unless you tell it to. Without Replace the destination if it exists, a destination that is already there stops the node and says so — which is what you want when the file nobody has imported yet is sitting at that path. With it on, the old file is gone, and there is no recycle bin on a file server.
Delete needs The path is a folder before it will touch a folder at all, and Delete everything inside it too before it will empty one. A folder delete without the second option requires the folder to already be empty. Check the path first: an expression that resolves to an empty string is how a delete ends up pointing at a parent folder.
Every path a step touched is listed in the run record as affectedPaths — including every file a
recursive delete removed, gathered before the delete rather than after.
Limits and errors
Transfers are capped at 25 MB in each direction, and a downloaded file is kept for as long as any other file a workflow produces. The connect timeout (20 seconds by default, up to 120) bounds reaching the server; the transfer timeout (120 seconds by default, up to 900) bounds one transfer or one listing. A listing is capped at 5,000 entries and 20 levels deep, and a larger one is refused rather than truncated — a truncated listing read by an If/Else is a wrong answer that looks right.
A symbolic link is listed, never followed: a recursive List will not walk through one, and a recursive Delete will not delete through one.
Where it can connect
A workflow may only reach file servers outside this platform's own network. On Elizon's hosted
service a server on a private network address is refused; a self-hosted deployment can allow them
(EGRESS_ALLOW_PRIVATE_HOSTS), which is the setting for your own file server on your own network.
Cloud metadata addresses and this platform's own services are refused either way.
Example
Pick up last night's ERP export and summarise it:
name: ERP export
description: Downloads the nightly export from the ERP's SFTP server.
graph:
nodes:
- id: nightly
type: trigger_recurrence
position: { x: 0, y: 0 }
data: { mode: cron, cronExpression: 0 6 * * *, timezone: Europe/Sofia }
- id: download
type: ftp
position: { x: 280, y: 0 }
data:
operation: download
path: /exports/orders.csv
connectTimeoutSeconds: 20
transferTimeoutSeconds: 120
edges:
- { id: nightly-to-download, source: nightly, target: download }The connection — protocol, server, login and host key — is not in a portable definition; whoever builds or installs the workflow sets it on the node.
In a Package
The connection belongs to the installer, so it is never carried: after installing, open the node and set the server, the login (as a secret reference) and, for SFTP, the host key.
- Travels:
operation,path,recursive,uploadSource,uploadFileExpression,uploadText,newPath,createDirectories,overwrite,deleteFolder,deleteRecursive,connectTimeoutSeconds,transferTimeoutSeconds - Converted: none
- Never carried:
protocol,host,port,username,password,privateKey,passphrase,hostKeyFingerprint