> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dualentry.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SFTP Bank Connections: File-Based Bank Feeds

> Connect a bank that delivers transaction files over SFTP, schedule a daily pickup, and import BAI2, MT940, or CSV files as bank transactions.

An SFTP bank connection is a file-based bank feed. Your bank drops a transaction file into a folder it hosts, DualEntry collects that file on a schedule, and the transactions land in DualEntry the same way they would from an automated feed: ready for [Bank Match](../../core-financials/close-management/bank-match/index) and reconciliation.

Think of the folder as a shared drive that only your bank and DualEntry can reach. Banks commonly offer one where a modern API connection is not available, which makes SFTP the usual answer for institutions a certified aggregator does not cover. It is self-serve: you set it up yourself from Bank Connections, with no custom build from DualEntry.

## Prerequisites

Most of the work happens at your bank, so start there rather than in DualEntry.

* **Confirm your bank will host an SFTP folder.** Ask your banker whether they can deliver a daily transaction file over SFTP, and confirm which file format they will send. Both answers come from the bank, and the format determines nothing you configure in DualEntry but everything about whether the file imports cleanly.
* **Get the connection details.** You need the host, the username, and the password the bank issues. The port and the folder path are optional; DualEntry defaults to port 22 and the root folder.
* **Ask about IP allowlisting.** Some banks only accept connections from known addresses. If yours does, ask DualEntry support for the addresses to give your bank.
* **Check the cost.** Banks often charge for an SFTP feed. DualEntry charges nothing extra for this connection method, so any fee you are quoted is the bank's.

You do not need the folder to contain anything yet. You can connect first and pick up files once the bank starts delivering them.

## Supported file formats

DualEntry reads three formats, and works out which one it has from the file extension.

| Format | Extensions               | Notes                                                                                    |
| ------ | ------------------------ | ---------------------------------------------------------------------------------------- |
| BAI2   | `.bai`, `.bai2`          | The common US bank reporting format.                                                     |
| MT940  | `.mt940`, `.sta`, `.940` | The SWIFT statement format, common outside the US.                                       |
| CSV    | `.csv`                   | Must match DualEntry's template. An arbitrary CSV layout from your bank will not import. |

A `.txt` file also works. DualEntry reads the first line and recognizes BAI2, MT940, or a CSV carrying the required headers. Any other extension, or a `.txt` whose contents match none of the three, fails with `Unsupported SFTP file format`.

If your bank can send BAI2 or MT940, prefer either over CSV. Both are standard formats the bank already produces correctly, whereas a CSV has to match DualEntry's column layout exactly, which means asking the bank to produce a custom file.

## Prepare a CSV file

Skip this section if your bank sends BAI2 or MT940. A CSV has to match DualEntry's template exactly, and the way to get that is to hand the template to your bank rather than reformat their export yourself.

Open the connection's settings, find **Example CSV format**, and choose **Download template**. Send that file to your bank as the layout to produce. Whoever edits it should leave the column headings alone, since the headings are what DualEntry matches on.

The template's columns:

| Column                   | Required        | Notes                                                                                                                                                             |
| ------------------------ | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `trx_id`                 | Yes             | The bank's unique ID for the transaction. Transactions are keyed on it, so a repeated `trx_id` updates the existing transaction instead of creating a second one. |
| `account_id`             | Yes             | The bank account the transaction belongs to. DualEntry creates a bank account per distinct value on first import.                                                 |
| `amount`                 | Yes             | Positive or negative, and never zero. Commas are tolerated.                                                                                                       |
| `posted_at`              | Yes in practice | The date the transaction posted. A row with no `posted_at` is treated as unposted and skipped rather than imported.                                               |
| `date`                   | No              | The transaction date. Falls back to `posted_at` when empty.                                                                                                       |
| `account_name`           | No              | A readable name for the account. Falls back to `account_id`.                                                                                                      |
| `description`            | No              | Free text carried onto the transaction.                                                                                                                           |
| `payee`                  | No              | Free text carried onto the transaction.                                                                                                                           |
| `reference`              | No              | Free text carried onto the transaction.                                                                                                                           |
| `is_posted`              | No              | `true` or `false`. A row set to `false` is skipped. Any other value fails the file.                                                                               |
| `currency_iso_4217_code` | No              | Defaults to USD when empty.                                                                                                                                       |

A file missing `trx_id` or `account_id` in its header row is rejected before any row is read, with the message `CSV is not the SFTP bank template`. That is the error to expect if someone drops the bank's own export into the folder unmodified.

## Connect the bank

Once you can access the folder with the credentials, the DualEntry side takes a minute.

1. Navigate to **Configuration → Company → Bank Connections**.
2. Choose **Add Bank Connection**, then **Add SFTP connection** from the menu.
3. Enter the **Bank name**. This is what the connection is called in DualEntry, so use the name your team will recognize.
4. Enter the **Host**, **Username**, and **Password** from your bank.
5. Optionally set the **Port** and the **Remote path**. Leave them empty for port 22 and the root folder.
6. Optionally add the **Bank URL**, which is a convenience link to the bank's site.
7. Save. The connection appears in your bank connections list.

To change credentials later, reopen the connection's settings and update them there. Leave the password blank to keep the current one. Changing credentials replaces what every future pickup uses, so coordinate the change with whoever manages the folder at the bank.

## Schedule pickups

A connected SFTP folder is collected once a day, and you choose when.

Open the connection and set the **Pickup schedule** hour. The times are listed in your own time zone, so pick the hour that comes after your bank finishes writing the day's file. Getting this wrong is the most common cause of a feed that looks a day behind: if DualEntry collects at 06:00 and the bank writes at 08:00, every pickup gets yesterday's file.

Choose **Pick up now** whenever you need the file immediately rather than waiting for the scheduled hour, which is what you will use during setup and at period end.

Each pickup reads every file in the folder, not just the newest. DualEntry fingerprints the contents of each file, so a file it has already collected is skipped rather than imported twice. That means your bank can leave files in place, and you can safely run **Pick up now** as often as you like. Files above 50 MiB are rejected.

## Review pickup history

Every pickup is recorded on the connection's history, whether it succeeded or not. Each row shows when the file was picked up, the filename, how many transactions were imported, the status, any error, and the source: **Scheduled** for the daily run, **Manual** for a **Pick up now**.

The five statuses:

* **Pending**: collected and queued, not yet imported.
* **Running**: importing now.
* **Completed**: every row imported.
* **Pickup failed**: DualEntry could not retrieve the file. Nothing was imported.
* **Import failed**: the file arrived but at least one row was rejected.

Two downloads sit on the history rows and are worth knowing about before you need them. The input file gives you exactly what DualEntry collected, which settles any question about what the bank actually sent. The error log, present on a failed import, lists each rejected row with the reason it failed.

When the most recent pickup failed to retrieve a file, the history offers **Retry pickup now** on that row, so a transient network or credential problem does not mean waiting for tomorrow.

## What happens when rows fail

An import failure is rarely all or nothing, which is the behavior to understand before you go looking for missing transactions.

When some rows are valid and others are not, the valid rows import and the rejected ones do not. The pickup shows **Import failed** with a count of the rows that failed, even though the transactions from the good rows are already in DualEntry. Download the error log to see which rows were rejected and why; each entry names the row and its error, such as `amount cannot be zero` or `trx_id is required on every data row`.

Fix the underlying problem, which usually means asking the bank to correct the file or correcting the CSV yourself, then put the corrected file in the folder and run **Pick up now**. Because transactions are keyed on `trx_id`, re-importing a file that contains rows you already have updates those transactions rather than duplicating them.

A whole-file failure works differently. If the format is unreadable or the CSV headers are wrong, nothing imports and the error sits on the pickup itself rather than in a per-row log.

## Map accounts to your ledger

SFTP accounts flow into the same GL-mapping step as every other bank connection, with one difference: DualEntry creates the bank account itself as files arrive, rather than you connecting to an account it already knows about. DualEntry creates a bank account for each distinct `account_id` it sees in the file, named from `account_name` where the file provides one. Those accounts appear in Bank Connections alongside accounts from any other connection type.

Mapping each bank account to its GL account works exactly as it does for every other connection, and the transactions then flow into Bank Match for reconciliation. See [banking connections](./banking-connections) for the account mapping step and the operational detail shared across connection types.

Watch the first import for accounts you did not expect. A bank that writes several accounts into one file creates all of them on the first pickup, and an `account_id` that changes format at the bank's end creates a second account rather than updating the first.

## Related reading

* [Banking connections](./banking-connections): connection paths, account mapping, status handling, and the security posture shared by every bank feed
* [Bank Match](../../core-financials/close-management/bank-match/index): reconciling the transactions an SFTP feed brings in
* [Bulk import](../automation-and-developer-tools/bulk-import): one-off historical loads, as opposed to a recurring feed
* [Integrations](../index): the full connector catalog
