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

# SFTP

> Automatic data delivery to your own SFTP server after every workflow run

Kadoa uploads your workflow results to an SFTP (SSH File Transfer Protocol) server
that you host, after every completed run. Use it when your systems read files from
a directory rather than from an object store or a warehouse.

Kadoa's support team sets up SFTP delivery for you. There is no self-service
connector screen for SFTP yet.

## Before you begin

Your server must meet all of these requirements.

| Requirement                         | Why                                                                     |
| ----------------------------------- | ----------------------------------------------------------------------- |
| Reachable from the public internet  | Kadoa connects outbound from changing addresses                         |
| No source-IP allowlist              | Kadoa cannot publish a fixed egress address for SFTP today              |
| One of four host-key algorithms     | `ssh-ed25519`, `ecdsa-sha2-nistp256`, `rsa-sha2-256`, or `rsa-sha2-512` |
| Supports `posix-rename@openssh.com` | Kadoa replaces files atomically and never overwrites in place           |
| Password or SSH private-key login   | Keyboard-interactive and multi-factor login are not supported           |
| An existing base directory          | Kadoa never creates the base directory itself                           |

If your server needs an allowlist, use [Cloud Storage](/docs/integrations/cloud-storage)
instead. Ask your Kadoa contact if you are unsure whether your server qualifies.

## Prepare the server

1. Create a dedicated account for Kadoa. Do not reuse a person's login.
2. Create the base directory that Kadoa writes into, for example `/incoming/kadoa`.
3. Give the account **write**, **create**, and **delete** permission on that directory
   and everything below it. Kadoa needs delete permission to clear its own temporary
   files.
4. Confirm that the account can create subdirectories. Kadoa creates one directory per
   workflow run below the base directory.
5. Record the server's host-key algorithm and SHA-256 fingerprint. Run this on the
   server itself, not from a client that has never connected:

   ```bash theme={null}
   ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
   ```

   The command prints the key size, then the fingerprint in the form
   `SHA256:<43 characters>`, then a comment. Send Kadoa the fingerprint exactly as
   printed, including the `SHA256:` prefix.

<Warning>
  Send the fingerprint through a channel that is separate from the server address, and
  confirm it with someone who has access to the server. The fingerprint is what proves
  Kadoa is talking to your server and not to an impostor. Kadoa refuses the connection
  when the presented key does not match, before it sends any credential.
</Warning>

## Add your credentials

Store the login credential as a secret variable first. Kadoa never accepts a password
or private key through email, chat, or a ticket.

1. Open **Team settings** and select **Variables**.
2. For a password, select **Create variable**, enter a key such as `SFTP_PASSWORD`,
   choose **Secret**, and enter the value.
3. For an SSH private key, store the key in your Google Secret Manager project.
   Then create a customer-managed Secret Variable that references that secret.
4. Select **Save**.

Repeat for the passphrase if your private key is encrypted. That passphrase needs its
own secret variable.

Secret values are write-only. Nobody, including Kadoa staff, can read a stored value
back. Do not paste a private key into the Kadoa-managed value field because it does
not preserve multiline PEM content. See [Secret Variables](/docs/workflows/secrets)
for provider choices and rotation.

## Give Kadoa the connection details

Send these non-secret details to your Kadoa contact:

| Detail                              | Example                      |
| ----------------------------------- | ---------------------------- |
| Host                                | `sftp.example.com`           |
| Port                                | `22`                         |
| Username                            | `kadoa-exports`              |
| Authentication method               | Password, or SSH private key |
| Variable key holding the credential | `SFTP_PASSWORD`              |
| Host-key algorithm                  | `ssh-ed25519`                |
| Host-key fingerprint                | `SHA256:...`                 |
| Base directory                      | `/incoming/kadoa`            |
| Formats you want                    | Parquet, JSONL, CSV, JSON    |

Also send your expected file sizes, your server's idle timeout, and any limit on
concurrent sessions. Kadoa sets its connection timeouts from those numbers.

Kadoa creates the connector **disabled**, tests it, and enables delivery only after a
verified first run.

## Formats and file layout

Choose any combination of these formats. Kadoa writes one file per format per run.

| Format  | File           | Use case                            |
| ------- | -------------- | ----------------------------------- |
| Parquet | `data.parquet` | Analytics and warehouse loads       |
| JSONL   | `data.jsonl`   | Streaming and line-based processing |
| JSON    | `data.json`    | APIs and integrations               |
| CSV     | `data.csv`     | Spreadsheets and legacy systems     |

If you do not choose formats, Kadoa delivers Parquet and JSONL.

Files land below your base directory in a directory per run:

```
/incoming/kadoa/{teamId}/{workflowId}/{runDatetimeSafe}-{runId}/
├── data.parquet
├── data.jsonl
├── data.json
└── data.csv
```

Ask Kadoa for a different layout if you need one. The path can use `{teamId}`,
`{workflowId}`, `{runId}`, `{runDatetimeSafe}`, `{runDatetime}`, and `{runDate}`.

## How delivery works

Kadoa uploads each file to a temporary name that ends in `.part`, waits for the server
to confirm the write, then renames it to the final name with
`posix-rename@openssh.com`. The rename is atomic, so a reader never sees a partial
file, and a repeated run replaces the previous file in one step.

**Only read files that do not end in `.part`.** A file with that suffix is still being
written or was left by an interrupted run.

Kadoa makes at most five connection attempts for one run, waiting 2, 5, 10, and 20
seconds between them. A run that already delivered some formats resumes with only the
missing ones. Errors that cannot improve by retrying, such as a wrong fingerprint,
a rejected login, or a missing base directory, stop after the first attempt.

A failed SFTP delivery never fails the workflow itself. The run still completes and
its data stays available through every other integration.

Preview runs and quality-assurance runs are not delivered.

## Verify the first delivery

1. Ask Kadoa to run the workflow once with the connector enabled.
2. List the run directory on your server and confirm one file per chosen format.
3. Confirm that no file ends in `.part`.
4. Compare the record count in the delivered file against the run's record count in
   the Kadoa dashboard.
5. Run the workflow a second time and confirm that the new run writes a new directory.

Tell your Kadoa contact if any step does not match. Kadoa keeps a per-run delivery
history, including the attempted formats, the number of attempts, and a failure
category.

## Rotate or replace credentials

1. Update the value in **Team settings → Variables**. Rotate the existing variable
   rather than creating a new one, so the connector keeps working.
2. Ask Kadoa to run the connection test.
3. Retire the old credential on your server only after the test passes.

Kadoa blocks deletion of a variable while a connector still uses it. The request fails
with a conflict, and the connector keeps running.

## Troubleshoot

| What you see                                             | Likely cause                                                      | What to do                                                  |
| -------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------- |
| Kadoa reports a server identity mismatch                 | The host key changed, or the fingerprint was recorded incorrectly | Send the current fingerprint from the server                |
| Kadoa reports an authentication failure                  | The password or key changed, expired, or the account is locked    | Rotate the secret variable, then ask for a retest           |
| Kadoa reports a denied file operation                    | The account cannot write, create, or delete in the base directory | Grant the missing permission                                |
| Kadoa reports an invalid destination path                | The base directory does not exist or was renamed                  | Recreate it, or send the new path                           |
| Kadoa reports that atomic replacement is unsupported     | The server does not offer `posix-rename@openssh.com`              | Enable the OpenSSH extension, or switch to Cloud Storage    |
| Kadoa reports that the server could not be reached       | A firewall, a new allowlist, or an outage                         | Confirm the endpoint is reachable from outside your network |
| Kadoa reports that the export file could not be produced | A Kadoa-side failure, not your server                             | Contact support. No action is needed on the server          |
| A `.part` file is left behind                            | A connection dropped mid-upload                                   | Delete it. Kadoa never reads or reuses it                   |

## Limits

* No source-IP allowlisting. Kadoa cannot give you a fixed egress address for SFTP.
* Your server must be reachable from the public internet. Kadoa cannot reach a private
  network, a VPN-only host, or a private IP address.
* Password and SSH private-key login only.
* The connection test is advisory. It proves a login and a file round trip at that
  moment. It does not guarantee the next delivery.
* A connection lost at the end of an upload can leave one `.part` file behind. Paths
  are unique per run, so no later run reuses or cleans that file.

## Get help

Submit a request through the [Support Center](/docs/support-center). Include the
workflow, the run time, and what you saw on the server. Never include a password, a
private key, or a passphrase in a support request.
