SFTP in Calculations in TAS 5.7
A guide for consultants who need to send a file to an SFTP server, or download a file from one, from a task calculation in TAS 5.7. Version 5.7 has no separate ftp API. File transfer is done with the curl object (node-libcurl library). The axios object supports only HTTP and HTTPS. The procedure was verified on a TAS 5.7.105 instance. Reading was tested against an SFTP server (listing, downloading text and binary files, error states, password from a secret). Writing (upload, rename, creating and deleting a directory, downloading back) was tested against an FTP server as described in chapter 9. The upload and QUOTE command mechanism is the same for both protocols; only the command names differ.
curl settings in 5.7 support SFTP (SSH, port 22). For classic FTP (port 21), you must also set the PROTOCOLS_STR option in the calculation, otherwise curl.perform() fails with the error Unsupported protocol. The procedure is in chapter 9.What 5.7 can and cannot do
Requirement |
TAS 5.7 |
How |
HTTP/HTTPS REST calls |
yes |
|
SFTP – upload, download, listing, deleting |
yes |
|
Classic FTP (port 21) |
yes |
|
Separate |
no |
from version 5.12.22, see FTP a SFTP použití ve výpočtech (in Czech) |
SmartEvent "FTP requester" |
no |
only in newer versions |
Behaviour of the curl object in 5.7
- The
curlobject is available only in task calculations (Task JS) and in identity calculations. It is not available in case calculations or in the calculations console. - The default protocols are HTTP, HTTPS and SFTP. FTP is enabled with the
PROTOCOLS_STRoption (chapter 9). - After the first
curl.perform(), a warning thatCurlApiis deprecated is written to the backend log. It is only a warning; the functionality works normally. - The pre-upgrade template check (
system.validateTemplate(tprocId)) reports every use ofcurl.*with the text "removed, use FtpApi". Ignore this in 5.7; it is preparation for the upgrade. curl.perform()closes the connection when it finishes. For another request in the same calculation, callcurl.start()again, otherwise it fails with the errorCurl handle is closed.- The result of
perform()is an object{ statusCode, data, headers }. For SFTP,statusCodeis always0; errors are raised as exceptions. - The default timeout is 60 s. For larger files, set
curl.setOpt('TIMEOUT', 600).
1. Verifying that the server has SFTP in libcurl
Run this in a test calculation on a task (the curl object is not available in the calculations console):
vars['vysledek'].setValue(curl.info());
The output must include sftp in the list of protocols and libssh2 (or libssh) among the libraries. The standard 5.7 image has libcurl/7.86.0 … libssh2/1.10.0 and scp, sftp in the protocols. If they are missing, the image has no SSH support and this must be resolved at the installation level.
2. Credentials
Three options, from the simplest to the most secure.
a) In the URL
Suitable only for testing; the password is in the template text.
curl.setOpt('URL', 'sftp://uzivatel:heslo@sftp.example.com:22/export/soubor.txt');
Special characters in the password must be URL-encoded (@ → %40, : → %3A, / → %2F). It is easier to use encodeURIComponent(heslo).
b) With the USERPWD option
The URL then contains no credentials and no encoding is needed.
curl.setOpt('URL', 'sftp://sftp.example.com:22/export/soubor.txt');
curl.setOpt('USERPWD', 'uzivatel:heslo');
c) From a secret
The password is not in the template code. The secret can have any name and its value is the string uzivatel:heslo. It is set either in local.js under the secrets key, or once via a calculation (the value is stored in the instance configuration; then remove the calculation from the template):
await lib.setSecret('sftpExport', 'uzivatel:heslo');
In the calculation, instead of curl.perform(), call curl.performSecureAuth(nazevSecretu), which loads the secret, sets USERPWD and performs the request:
curl.setOpt('URL', 'sftp://sftp.example.com:22/export/soubor.txt');
const response = await curl.performSecureAuth('sftpExport');
USERPWD is visible to anyone who is allowed to edit the template.Key instead of password
libcurl supports the SSH_PRIVATE_KEYFILE and SSH_PUBLIC_KEYFILE options (path to a file on the TAS server, typically in a mapped volume). This option is not covered by platform tests; it works at the libcurl level.
3. Downloading a file from SFTP
A text file; the response arrives as a string:
curl.start();
curl.setOpt('URL', 'sftp://sftp.example.com:22/export/data.csv');
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('TIMEOUT', 300);
const { data } = await curl.perform();
vars['csvObsah'].setValue(data);
A binary file (PDF, XLSX, ZIP) saved to the case DMS. Without curl.enable('Raw'), the content is corrupted by conversion to a string (verified: a 19,156 B PNG arrived as a string of 18,136 characters):
curl.start();
curl.setOpt('URL', 'sftp://sftp.example.com:22/export/faktura.pdf');
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('TIMEOUT', 300);
curl.enable('Raw');
const { data } = await curl.perform(); // data je Buffer
const dmsfId = await lib.storeAttachment('faktura.pdf', data);
lib.storeAttachment(nazev, Buffer) saves the file as an attachment of the current case and returns its DMS ID. If you want to assign it to an attachment variable, set the variable to the returned ID.
4. Uploading a file to SFTP
Content created in the calculation, for example CSV:
const csv = 'id;nazev\n1;Test\n';
const datum = new Date().toISOString().slice(0, 10); // 2026-11-09
curl.start();
curl.setOpt('URL', 'sftp://sftp.example.com:22/import/export_' + datum + '.csv');
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('TIMEOUT', 300);
curl.setOpt('UPLOAD', true);
curl.sendBinaryBuffer(Buffer.from(csv, 'utf-8'));
await curl.perform();
An attachment from the DMS (attachment variable, file name or DMS ID):
const obsah = await storage.getDmsContent(vars['priloha'], 'buffer'); // Buffer
curl.start();
curl.setOpt('URL', 'sftp://sftp.example.com:22/import/' + encodeURIComponent('smlouva.pdf'));
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('TIMEOUT', 600);
curl.setOpt('UPLOAD', true);
curl.sendBinaryBuffer(obsah);
await curl.perform();
UPLOAD and sendBinaryBuffer before perform. The target directory must exist on the server; SFTP does not create it during upload (see chapter 6). Without write permission, perform() fails with the error Access denied to remote resource. Upload via UPLOAD + sendBinaryBuffer and downloading back were verified against an FTP server (35 B including diacritics, identical content).5. Directory listing
The URL must end with a slash. The default output is a long format like ls -l; the DIRLISTONLY option returns only names, one per line:
curl.start();
curl.setOpt('URL', 'sftp://sftp.example.com:22/export/');
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('DIRLISTONLY', true);
const { data } = await curl.perform();
const soubory = data.split('\n').filter(n => n && n !== '.' && n !== '..');
return soubory;
Without DIRLISTONLY, the listing arrives in the form -rw------- 1 demo users 379 Sep 19 2023 readme.txt, one entry per line, including . and ...
6. Deleting, renaming, creating a directory
libcurl can run commands on SFTP via the QUOTE option (before the transfer) or POSTQUOTE (after the transfer). Supported commands: rm, rmdir, rename, mkdir, chmod, ln, symlink. The commands are sent immediately after login, before changing to the directory from the URL, so use absolute paths in them; a relative path is resolved against the account root. If a command fails on the server (missing permissions, non-existent path), perform() throws Quote command returned error.
Deleting a file after a successful download (the "process and clean up" pattern):
curl.start();
curl.setOpt('URL', 'sftp://sftp.example.com:22/export/data.csv');
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('POSTQUOTE', ['rm /export/data.csv']);
const { data } = await curl.perform();
Moving to an archive after upload:
curl.setOpt('POSTQUOTE', ['rename /import/data.csv /import/archiv/data.csv']);
Creating a directory before upload:
curl.setOpt('QUOTE', ['mkdir /import/2026-11']);
Commands only, without a transfer, for example to delete a file without downloading anything:
curl.start();
curl.setOpt('URL', 'sftp://sftp.example.com:22/export/');
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('NOBODY', true);
curl.setOpt('QUOTE', ['rm /export/stary.csv']);
await curl.perform();
7. Error handling
On error, curl.perform() throws an exception with the libcurl error text, for example Couldn't connect to server, Login denied, Timeout was reached, Unsupported protocol, Access denied to remote resource (write without permission) or Quote command returned error. Recommended pattern:
try {
curl.start();
curl.setOpt('URL', 'sftp://sftp.example.com:22/export/data.csv');
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('TIMEOUT', 120);
const { data } = await curl.perform();
vars['csvObsah'].setValue(data);
} catch (err) {
throw new Error('SFTP stažení selhalo: ' + err.message);
}
The error is then shown to the user in the task and written to the calculations log.
8. Common problems
Symptom |
Cause and solution |
|
The URL starts with |
|
Wrong password or unencoded special characters in the URL. Use |
The downloaded PDF is corrupted |
|
The second request in the same calculation fails |
Error |
|
The account has no write permission for the target path, or the target directory does not exist. |
Upload of a large file ends with a timeout |
The default limit is 60 s; set |
Upload to a non-existent directory fails |
SFTP does not create directories; use |
9. Classic FTP
For ftp:// URLs in 5.7, you must set the PROTOCOLS_STR option with a list of protocols after curl.start(). Without it, perform() fails with the error Unsupported protocol. Verified on 5.7.105: listing, upload, downloading back, MKD, renaming, DELE and RMD.
ftp API, rewrite this code according to chapter 10.Directory listing and upload differ from SFTP only in the protocol in the URL and one extra line:
curl.start();
curl.setOpt('PROTOCOLS_STR', 'ftp,sftp,http,https');
curl.setOpt('URL', 'ftp://ftp.example.com/import/' + encodeURIComponent('data.csv'));
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('TIMEOUT', 300);
curl.setOpt('UPLOAD', true);
curl.sendBinaryBuffer(Buffer.from(csv, 'utf-8'));
const r = await curl.perform(); // r.statusCode je FTP kód odpovědi, např. 226
For FTP, the QUOTE commands are classic FTP commands, again with absolute paths:
Operation |
SFTP |
FTP |
create a directory |
|
|
rename / move |
|
|
delete a file |
|
|
delete a directory |
|
|
curl.start();
curl.setOpt('PROTOCOLS_STR', 'ftp,sftp,http,https');
curl.setOpt('URL', 'ftp://ftp.example.com/import/');
curl.setOpt('USERPWD', 'uzivatel:heslo');
curl.setOpt('NOBODY', true);
curl.setOpt('QUOTE', ['MKD /import/archiv', 'RNFR /import/data.csv', 'RNTO /import/archiv/data.csv']);
await curl.perform();
10. What changes after the upgrade
From version 5.12.22, the ftp API is available, which also supports classic FTP, and curl is removed in newer versions. The template check reports this before the upgrade. When migrating from 5.7, simply replace the curl.* blocks with calls to the corresponding client method:
const client = ftp.getFtpClient({
protocol: 'sftp', // nebo 'ftp'
host: 'sftp.example.com',
port: 22,
username: 'uzivatel',
password: 'heslo',
});
await client.list('/export');
await client.upload(vars['priloha'], '/import/smlouva.pdf'); // zdroj je DMS soubor
await client.download('/export/data.csv'); // vrátí lokální cestu
await client.exists('/export/data.csv');
await client.delete('/export/data.csv');
await client.rename('/import/a.csv', '/import/archiv/a.csv');
await client.createDirectory('/import/2026-11', true);
await client.removeDirectory('/import/old', true);
From version 5.17, the password can be passed from the Vault via ftp.getFtpClientWithVaultPassword(config, vault.get('nazev')). A complete description of the new API, including allowed paths, is in the article FTP a SFTP použití ve výpočtech (in Czech).