File drops (SFTP and S3)
Ce contenu n’est pas encore disponible dans votre langue.
In short
Section titled “In short”Many banks and payment providers already produce end-of-day files: a statement export, a transaction extract, a customer list. Instead of writing an integration, you can leave those files on an SFTP server or in an S3-compatible bucket (AWS S3, MinIO, Ceph), and SpeculaPro collects them on a schedule.
Each file is loaded once. Every row goes through exactly the same checks, currency conversion, encryption and duplicate protection as a record sent to the ingest API, and gets the same per-row result. When a file is done, SpeculaPro writes a results file next to it (one line per row: loaded, already loaded, or rejected with the reason), and then moves, tags or leaves the original.
| Format | Carries | Read as |
|---|---|---|
| CSV | Transactions or customers | The same columns as Settings → Data import. See Importing a file: the columns |
| JSON Lines | Any kind: transactions, customers, accounts, account events, FX rates | One JSON object per line, exactly as the API’s items |
| ISO 20022 camt.053 | Transactions | Bank-to-customer statements, versions .001.02 to .001.13. See ISO 20022 files |
| ISO 20022 pain.001 | Transactions | Credit transfer instructions, versions .001.03 to .001.12. See ISO 20022 files |
Any of them may be gzip-compressed (.gz).
File drops are set up per workspace by an administrator, under Settings → Data sources. See
Data sources. On a self-hosted installation, the connector role
must be running; see Connector.
How a run works
Section titled “How a run works”Every few minutes (you choose: from every 5 minutes to once a day), a connector:
- Lists the directory (SFTP) or prefix (S3). Only files directly inside are read; sub-folders are not.
- Skips names it won’t load: hidden files (starting with
.), names with/,\,..or control characters, names over 255 bytes, results files (*.results.csv), and anything that doesn’t match the file name pattern. A pattern such as*.csvalso matchesname.csv.gz. - Leaves for next time any file changed in the last 30 seconds: it may still be uploading.
- Skips without downloading any file it has already loaded or failed for good, recognised by name, size and its S3 ETag or SFTP modification time.
- Downloads the file to local disk while computing its SHA-256. The file is never held in memory, and loading reads this stable copy even if the original changes meanwhile.
- Claims the file by name, size and SHA-256. If the same file was loaded before, or another connector is loading it, it stops here. A file with the same name but different content is a new file and is loaded.
- Loads it: rows are read as a stream and sent in batches through the ingest checks, on four
parallel workers by default. All the rows for one
external_idgo to the same worker, so a customer’s later version is applied after its earlier one. - Writes the results file back (if you set a results path) and keeps a copy for download in the app.
- Moves, tags or leaves the original (see After loading).
Files are taken oldest first, up to 100 a run by default, so end-of-day files load in the order they were produced.
How to set up a file drop
Section titled “How to set up a file drop”You need the Data sources permission (datasources.manage), held by the Administrator role.
- On the server or bucket, create a directory or prefix for SpeculaPro to read, and give
SpeculaPro its own login:
- SFTP: a user that logs in with an SSH key. SpeculaPro doesn’t use passwords.
- S3: an access key with permission to list and read the prefix and, depending on what you choose below, to write results, copy and delete objects, or set object tags.
- Ask the server’s administrator for the SFTP host key: the public key line, such as
ssh-ed25519 AAAA…, or itsSHA256:fingerprint. SpeculaPro refuses any other server. - In SpeculaPro, open Settings → Data sources and select Add data source.
- Choose SFTP or Object storage (S3), what the files carry, and the format. Leave Detect from the file unless every file has the same format and you want to be strict.
- Fill in the connection details, the Directory or Prefix, and the File name pattern.
- Choose a Results path and what happens After loading.
- Choose how often to look for new files, then select Save.
- Open the new data source and select Test connection. It connects and counts the matching files, without loading anything.
- Select Run now to load what is waiting. The File history fills in as each file loads.
Step-by-step screens and every field: Data sources.
File formats
Section titled “File formats”CSV files use the same columns, headers and date rules as Settings → Data import, so a file that imports by hand also loads as a file drop. CSV covers transactions and customers only. For accounts, account events and FX rates, use JSON Lines.
Each CSV row may be up to 1 MiB, including quoted fields that span lines. A larger row (often a missing closing quote) is skipped while it is read, never held in memory, and reported on its line as row larger than 1048576 bytes (a missing closing quote?); skipped; the rest of the file still loads.
See Importing a file: the columns.
JSON Lines
Section titled “JSON Lines”One JSON object per line, each exactly what you would put in the items array of the matching API
call: transactions, customers,
accounts, account events or
FX rates. Blank lines are ignored, and each line may be up to 1 MiB.
{"external_id":"TX-20261009-000001","occurred_at":"2026-10-09T08:15:00Z","customer_external_id":"C-1001","direction":"out","type":"transfer","channel":"momo","amount_minor":150000,"currency":"GHS"}{"external_id":"TX-20261009-000002","occurred_at":"2026-10-09T08:16:12Z","customer_external_id":"C-2044","direction":"in","type":"deposit","channel":"agent","amount_minor":50000,"currency":"GHS"}ISO 20022
Section titled “ISO 20022”camt.053 statements and pain.001 credit transfer files are read as transactions, with every field mapped as described in ISO 20022 files.
How the format is chosen
Section titled “How the format is chosen”With Detect from the file, SpeculaPro looks at the name (after removing .gz), then at the
first bytes:
| Name ends with | Read as |
|---|---|
.csv, .txt |
CSV |
.jsonl, .ndjson, .json |
JSON Lines |
.xml |
camt.053 or pain.001, from the document’s start; any other XML is refused |
| Anything else | JSON Lines if it starts with {, ISO 20022 if it starts with <, otherwise CSV |
After loading
Section titled “After loading”| Choice | What happens to the original | Use when |
|---|---|---|
| Move to a processed folder | Moved to the Processed path. If a file of that name is already there, the moved file’s name is prefixed with the time, such as 20261009T180000Z-statement.xml |
You control the server, and want the input directory to show only what is waiting |
| Tag the object (S3 only) | Tagged speculapro-processed=<time> |
The bucket is read-only to SpeculaPro apart from tagging, or a lifecycle rule should expire loaded files |
| Leave it | Untouched | SpeculaPro has read-only access |
Moved files can be deleted automatically after a number of days (Keep (days); 0 keeps them). Only files SpeculaPro itself moved there are deleted: anything else in the processed path, such as your own copies, is never touched.
Tagging adds the speculapro-processed tag and keeps the object’s other tags. S3 allows ten
tags per object; if an object already has ten, tagging fails and the run reports it, but the file
is still recognised as loaded and isn’t loaded again.
A file whose rows are still waiting because SpeculaPro’s pipeline was briefly unavailable (status To retry) stays where it is, and the whole file is loaded again on the next run. Rows that were already loaded are recognised as duplicates.
The results file
Section titled “The results file”If you set a Results path, each loaded file gets a results file there named
<file name>.results.csv, written uncompressed. On SFTP it is uploaded under a hidden temporary
name and then renamed, so your systems never read half a file. It must differ from the input
directory; results files are never read as input.
line,external_id,status,errors2,TX-20261009-000001,accepted,3,TX-20261009-000002,rejected,expected value to be one of "in, out" (direction: sideways)4,TX-20261009-000003,duplicate,| Column | Meaning |
|---|---|
line |
The line in your file (for ISO 20022, the line where the entry or instruction starts) |
external_id |
The row’s key, if it could be read |
status |
accepted, duplicate, stale (an older version of a customer or account than one already stored), rejected or retry. See Responses and statuses |
errors |
Why a row was rejected or must be retried |
- Rows are written as they finish, not in file order: sort by
lineto read them in order. - A value starting with
=,+,-,@, a tab or a carriage return gets a leading', so a spreadsheet never runs it as a formula. Anexternal_idsuch as-123therefore appears as'-123. - A copy is kept in SpeculaPro for download from the file history, if its compressed size is up to
SP_CONNECTOR_MAX_STORED_RESULTS_BYTES(32 MiB by default). It is kept as long as the file history.
Limits
Section titled “Limits”| Limit | Default | Set by |
|---|---|---|
| Largest file | 2 GiB (900 MiB in the Helm chart) | SP_CONNECTOR_MAX_FILE_BYTES. A larger file is recorded as failed once, without being downloaded again |
| One CSV row | 1 MiB | Fixed. A larger row is skipped and reported; the rest of the file loads |
Largest content of a .gz file |
The same as the largest file | A file that decompresses to more fails with the decompressed file is larger than this deployment’s limit. This stops a small compressed file from filling the disk |
| Files per run | 100 | SP_CONNECTOR_MAX_FILES_PER_RUN. The rest load on the next run |
| One JSON Lines line | 1 MiB | Fixed |
| One ISO 20022 entry or instruction | 8 MiB | Fixed. A larger one is skipped and reported; the rest of the file loads |
| Interrupted loads of one file | 3 | Fixed. After the third, the file is failed and skipped |
| Problems listed per file | The first 200, by line | Fixed. The results file lists every row |
| How often | Every 5 minutes to once a day | The data source’s schedule |
| Data sources per workspace | 50 | Fixed |
Security
Section titled “Security”- SFTP servers are pinned. You give the server’s host key when you set it up, and any other key is refused, so nobody can impersonate the server. If the key changes, the run fails with host key mismatch: the server presented SHA256:… (ssh-ed25519), but SHA256:… is pinned: check the new key with the server’s administrator before updating it.
- Keys and secrets are encrypted with your workspace’s key and are never shown again or returned by the API, only whether they are set.
- Internal addresses are refused unless your operator allows them: names such as
localhostor*.internal, and private, link-local or cloud-metadata addresses. The check runs when you save and again on every connection, after the name is resolved. - Plain-http S3 endpoints are refused unless your operator allows them.
- Remote names are never used as local paths. Files are staged under names SpeculaPro chooses.
- Stalled transfers fail after 60 seconds without progress, and connecting gives up after 15.
- Every change, test, run, download and loaded file is audited. See Audit log.