> For the complete documentation index, see [llms.txt](https://docs.doku.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.doku.com/subscription-and-billing/flexibill/account-billing/connectivity-and-file-security-sftp.md).

# Connectivity & File Security (SFTP)

{% hint style="info" %}
Availability: 🇮🇩 Indonesian Business Account only
{% endhint %}

This page covers all technical requirements for connecting to the DOKU SFTP server, encrypting batch billing files, and understanding the folder structure, file format, and report output for Merchant Hosted Scheduler batch operations.

***

## Features & Benefits

**🔐 Two-Layer File Encryption**

Batch files are protected with a two-layer encryption scheme — AES-256 for file content and RSA for key protection — ensuring that sensitive billing data is never exposed in transit or at rest on the SFTP server.

**📁 Dedicated Inbound and Outbound Directories**

DOKU provides separate folders on the SFTP server for uploading billing files and downloading result reports, with a clear naming convention that separates inbound from outbound operations.

**📊 Structured Result Reports**

After processing each batch, DOKU generates a `.TXT` report with a summary header and per-transaction detail rows — including response codes, approval codes, masked card numbers, and bank codes — for complete reconciliation.

**🔔 Processing Completion Notification**

DOKU notifies your merchant endpoint when the result report is ready, so your system does not need to poll the SFTP server.

***

## SFTP Connection

SFTP credentials are provisioned per merchant account and per environment (Sandbox / Production). Contact your **DOKU Account Manager** to obtain credentials.

<img src="/files/pahvafYRkikWzbdlerPN" alt="" height="325" width="624">

**Provide the following to DOKU when requesting access:**

* Your server's IP address(es) for whitelisting — SFTP access is restricted to pre-approved IPs only
* Your preferred authentication method: SSH key pair (recommended) or password

**Connection parameters provided by DOKU:**

<table><thead><tr><th width="170.6796875">Parameter</th><th>Description</th></tr></thead><tbody><tr><td><strong>Host</strong></td><td>DOKU SFTP server hostname</td></tr><tr><td><strong>Port</strong></td><td>SFTP port</td></tr><tr><td><strong>Username</strong></td><td>Your merchant SFTP username</td></tr><tr><td><strong>Authentication</strong></td><td>SSH private key or password, as configured during setup</td></tr></tbody></table>

**Recommended SFTP clients:**

<table><thead><tr><th width="164.58203125">Type</th><th>Options</th></tr></thead><tbody><tr><td>Command-line</td><td><code>sftp</code>, <code>scp</code></td></tr><tr><td>GUI</td><td>FileZilla, WinSCP, Cyberduck</td></tr><tr><td>Programmatic</td><td><code>paramiko</code> (Python), <code>JSch</code> (Java), <code>ssh2</code> (Node.js), or any standard SFTP library</td></tr></tbody></table>

{% hint style="warning" %}
SFTP credentials are environment-specific. Sandbox and Production credentials are different — never use Production credentials in a test environment. Contact your DOKU Account Manager immediately if credentials are compromised.
{% endhint %}

***

## Folder Structure

After connecting to the DOKU SFTP server, you will find two folders:

<table><thead><tr><th width="112.0234375">Folder</th><th width="193.20703125">Direction</th><th>Purpose</th></tr></thead><tbody><tr><td><code>/download</code></td><td><strong>Merchant uploads here</strong></td><td>Upload your encrypted batch billing files to this folder for DOKU to pick up and process</td></tr><tr><td><code>/upload</code></td><td><strong>Merchant downloads from here</strong></td><td>DOKU places result reports here after processing. Also where failed files land in the <code>/upload/failed</code> subfolder</td></tr></tbody></table>

{% hint style="info" %}
**The folder names are from DOKU's perspective**, not the merchant's. DOKU *downloads* files from `/download` (i.e., picks up your uploads). DOKU *uploads* result reports to `/upload` (i.e., places them for you to retrieve). Think of it as: you write to `/download`, you read from `/upload`.
{% endhint %}

<img src="/files/h9hS6ikAAQIGK2sqa6Dz" alt="" height="424" width="328">

**Subfolder for failures:**

<table><thead><tr><th width="170.46484375">Folder</th><th>Contents</th></tr></thead><tbody><tr><td><code>/upload/failed</code></td><td>Files that could not be processed — decryption failures, invalid filenames, or files where <code>EXECUTE DATE</code> is in the past. DOKU also sends a notification to your endpoint when a file lands here.</td></tr></tbody></table>

***

## File Encryption

All batch billing files must be encrypted before upload. DOKU uses a **two-layer encryption scheme**:

* **Layer 1 (Content):** File data encrypted with AES-256 using a symmetric key
* **Layer 2 (Key Protection):** The symmetric key itself encrypted with DOKU's RSA public key

This ensures that even if the encrypted file is intercepted, the content cannot be decrypted without DOKU's RSA private key.

<img src="/files/P1Jo4DsjMGJhEyXqcoW0" alt="" height="511" width="624">

#### Encryption Process — Step by Step

{% stepper %}
{% step %}

#### Generate random Symmetric Key using a SALT (exactly 21 characters)

Generate the symmetric key using a SALT that is exactly 21 characters.
{% endstep %}

{% step %}

#### Encrypt the file content with AES-256 using the Symmetric Key

Encrypt the file content with AES-256 using the generated symmetric key.
{% endstep %}

{% step %}

#### Encrypt the Symmetric Key using DOKU's RSA Public Key (No Padding)

Encrypt the symmetric key using DOKU's RSA public key with no padding.
{% endstep %}

{% step %}

#### Prepend the length of the Symmetric Key as a 4-character string: "128 "

Prepend the length of the symmetric key as a 4-character string: `"128 "`.
{% endstep %}

{% step %}

#### Append the Encrypted Symmetric Key to the end of the Encrypted File

Append the encrypted symmetric key to the end of the encrypted file, then upload the resulting file to SFTP `/download`.
{% endstep %}
{% endstepper %}

**Key requirements:**

<table><thead><tr><th width="176.84765625">Parameter</th><th>Requirement</th></tr></thead><tbody><tr><td><strong>SALT</strong></td><td>Exactly <strong>21 characters</strong> — any alphanumeric combination. This is a hard requirement — a SALT of any other length will cause encryption failure</td></tr><tr><td><strong>AES mode</strong></td><td>AES-256</td></tr><tr><td><strong>RSA padding</strong></td><td>No Padding</td></tr><tr><td><strong>DOKU Public Key</strong></td><td>Provided by DOKU during setup. Must be renewed <strong>annually or bi-annually</strong> — contact your Account Manager before expiry</td></tr><tr><td><strong>Key length prefix</strong></td><td><code>128</code> (4 characters) prepended to the final file</td></tr></tbody></table>

{% hint style="warning" %}
If you upload a file encrypted with an **expired DOKU public key**, DOKU cannot decrypt it. The file will be placed in `/upload/failed` and a failure notification will be sent to your endpoint. Re-encrypt with the current public key and re-upload with a new filename.
{% endhint %}

#### DOKU Encryption Utility (Java)

DOKU provides a Java JAR utility (`tkn-utility.jar`) for encrypting and decrypting files. The same algorithm can also be implemented in any language that supports AES-256 and RSA — you are not required to use the Java utility.

**Encrypt:**

```bash
java -jar target/tkn-utility.jar encrypt \
  <input_file> \
  <output_file> \
  <public_key_file> \
  <salt_21_chars>
```

**Example:**

```bash
java -jar target/tkn-utility.jar encrypt \
  /data/TKN_202510ABC.TXT \
  /data/TKN_202510ABC.enc.TXT \
  /keys/PUBLICKEY_120078_20210115111404.key \
  012345678901234567890
```

**Decrypt (for verifying report files or testing):**

```bash
java -jar target/tkn-utility.jar decrypt \
  <input_file> \
  <output_file> \
  <private_key_file>
```

**Example:**

```bash
java -jar target/tkn-utility.jar decrypt \
  /data/TKN_202510ABC.enc.TXT \
  /data/TKN_202510ABC.dec.TXT \
  /keys/PRIVATEKEY_120078_20210115111404.key
```

{% hint style="info" %}
The encrypt/decrypt utility is provided for convenience. The underlying encryption is standard AES-256 + RSA — you can implement it in any language. Use the JAR utility to validate your implementation in testing before building a programmatic integration.
{% endhint %}

***

## Batch File Format

Batch files submitted to `/download` must follow this format:

**File requirements:**

<table><thead><tr><th width="163.6796875">Requirement</th><th>Specification</th></tr></thead><tbody><tr><td><strong>Format</strong></td><td>Plain text (<code>.TXT</code>)</td></tr><tr><td><strong>Filename prefix</strong></td><td>Must begin with <code>TKN_</code> — e.g., <code>TKN_202510ABC.TXT</code></td></tr><tr><td><strong>Encoding</strong></td><td>UTF-8</td></tr><tr><td><strong>Encryption</strong></td><td>AES-256 + RSA as described above — plaintext files will not be processed</td></tr></tbody></table>

For the full column definition of the batch file content → [Merchant Hosted Scheduler — Column Definition](broken://pages/3a269c6dc7a5509300ffebaf02354b7577172047#column-definition)

***

## Report File Format

After DOKU processes a batch file, a result report in `.TXT` format is placed in the SFTP `/upload` folder. The report has two sections: a **summary block** at the top and **per-transaction rows** below.

#### Summary Block

The first rows of the report contain batch-level totals:

<table><thead><tr><th width="183.3671875">Field</th><th width="129.80859375">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>TOTAL AMOUNT</code></td><td>Number(12)</td><td>Sum of all successfully charged amounts in this batch</td></tr><tr><td><code>TOTAL TRANSACTION</code></td><td>Number(11)</td><td>Total number of records in the batch</td></tr><tr><td><code>TOTAL SUCCESS</code></td><td>Number(11)</td><td>Number of records charged successfully</td></tr><tr><td><code>TOTAL FAILED</code></td><td>Number(11)</td><td>Number of records that failed</td></tr></tbody></table>

#### Per-Transaction Rows

Each subsequent row represents one record from the batch file:

| Field               | Type              | Description                                                                                               |
| ------------------- | ----------------- | --------------------------------------------------------------------------------------------------------- |
| `CARD NUMBER`       | NS(16)            | Masked card number — e.g., `411111******7548`                                                             |
| `INVOICE NUMBER`    | Alphanumeric(64)  | Invoice number as submitted in the batch file                                                             |
| `AMOUNT`            | Number(12,2)      | Amount charged for this record                                                                            |
| `CURRENCY`          | Alpha(3)          | Currency code — e.g., `IDR`                                                                               |
| `RESPONSE CODE`     | Alphanumeric(2)   | Result of the charge attempt. `00` = success. Other codes indicate failure — see below                    |
| `RESPONSE MESSAGE`  | Alphanumeric(256) | Human-readable result description — e.g., `SUCCESS`, `INSUFFICIENT FUNDS`, `INVALID TOKEN`                |
| `APPROVAL CODE`     | Alphanumeric(16)  | Bank approval code for successful charges. Empty for failed records                                       |
| `PAYMENT DATE TIME` | Number(14)        | Timestamp of the charge attempt in `yyyyMMddHHmmss` format — e.g., `20251005121030` = 5 Oct 2025 12:10:30 |
| `BANK CODE`         | Number(3)         | Issuing bank identifier. See Bank Code lookup below                                                       |

#### Bank Code Lookup

<table><thead><tr><th width="145.19921875">Bank Code</th><th>Bank</th></tr></thead><tbody><tr><td><code>100</code></td><td>BNI</td></tr><tr><td><code>150</code></td><td>Bank CIMB</td></tr><tr><td><code>151</code></td><td>BRI</td></tr><tr><td><code>350</code></td><td>Bank Mandiri</td></tr><tr><td><code>400</code></td><td>BCA</td></tr></tbody></table>

#### PAYMENT DATE TIME Format

`yyyyMMddHHmmss` — parsed as follows:

```
20251005121030
│   │ │ │ │ └── Seconds: 30
│   │ │ │ └──── Minutes: 10
│   │ │ └────── Hours:   12
│   │ └──────── Date:    05
│   └────────── Month:   10
└────────────── Year:    2025
= 5 October 2025 at 12:10:30 WIB
```

#### RESPONSE CODE `00`

`RESPONSE CODE: 00` means the charge was processed successfully. Any other code indicates a failure. The full list of response codes and their meanings is available from your DOKU Account Manager.

👉 [Learn more](https://developers.doku.com/get-started-with-doku-api/response-code/http-status-and-case-code#id-2.-credit-card) of Bank and DOKU response code

***

## Failure Scenarios

#### Window 1 — Before Payment Execution (File-Level Failures)

These failures occur before any charge is attempted. The entire file fails — no records are processed.

<table><thead><tr><th width="123.68359375">Scenario</th><th width="216.80078125">Cause</th><th>Signal</th><th>Resolution</th></tr></thead><tbody><tr><td><strong>Decryption failure</strong></td><td>Wrong or expired DOKU public key used for encryption; incorrect SALT length; encrypted symmetric key not appended correctly</td><td>File placed in <code>/upload/failed</code>; DOKU sends failure notification to merchant endpoint</td><td>Re-encrypt using the correct current DOKU public key with a 21-character SALT; re-upload with a <strong>new filename</strong></td></tr><tr><td><strong>Invalid filename</strong></td><td>Missing <code>TKN_</code> prefix or wrong file extension</td><td>File not recognized; no processing occurs</td><td>Rename file with <code>TKN_</code> prefix and <code>.TXT</code> extension; re-upload</td></tr><tr><td><strong>Past EXECUTE DATE</strong></td><td>All records in the file have an <code>EXECUTE DATE</code> that has already passed</td><td>File placed in <code>/upload/failed</code> with <code>INVALID EXECUTE DATE</code>; DOKU sends failure notification</td><td>Correct <code>EXECUTE DATE</code> to today or a future date; re-encrypt and re-upload with a <strong>new filename</strong></td></tr></tbody></table>

{% hint style="warning" %}
When re-uploading after a file-level failure, always use a **new filename**. The failed filename cannot be reused — submitting the same filename to the Trigger API will return a duplicate error.
{% endhint %}

#### Window 2 — After Payment Execution (Record-Level Failures)

These failures occur during charge execution. Valid records are charged; failed records are flagged in the result report. Other records in the same file are not affected.

<table><thead><tr><th width="141.31640625">Scenario</th><th width="163.8828125">Signal in Report</th><th width="161.91796875">Token Still Valid?</th><th>Resolution</th></tr></thead><tbody><tr><td><strong>Insufficient funds</strong></td><td><code>FAILED</code> + bank decline code</td><td>✅ Yes</td><td>Retry the customer in the next billing cycle with the same token</td></tr><tr><td><strong>Expired card / invalid token</strong></td><td><code>FAILED</code> + token-related response code</td><td>❌ No</td><td>Initiate new First Payment with customer to obtain a new token; use new token in next cycle</td></tr><tr><td><strong>Card blocked or stolen</strong></td><td><code>FAILED</code> + restriction response code</td><td>❌ No</td><td>Contact customer for a new payment method; initiate new First Payment once resolved</td></tr><tr><td><strong>Already paid this month</strong></td><td><code>ALREADY_PAID</code></td><td>✅ Yes</td><td>Send One Bill/Month is active and customer was already charged this month — no action needed</td></tr></tbody></table>

***

## Terms & Conditions

* SFTP access requires **IP whitelisting** — provide all server IPs to DOKU during setup. If your IP changes, re-whitelist before the change takes effect
* Batch files must be encrypted using the current DOKU RSA public key — DOKU public keys must be renewed **annually or bi-annually**
* **SALT must be exactly 21 characters** — any other length will cause encryption failure
* Batch files must use the `TKN_` filename prefix and `.TXT` extension
* Raw card numbers, CVVs, and full PAN data must never appear in batch files — use DOKU-issued tokens only
* Outbound report files in `/upload` do not need to be encrypted — they are provided as plaintext `.TXT`

***

## FAQ

<details>

<summary>What is the SALT and what are the requirements?</summary>

The SALT is a random string used during the AES-256 symmetric key generation step of the encryption process. It must be **exactly 21 characters** — alphanumeric, any combination. Using a SALT of any other length will cause encryption failure and the file will land in `/upload/failed`. The SALT does not need to be stored permanently — it is consumed during encryption and is not needed for decryption.

</details>

<details>

<summary>Where do I get DOKU's RSA public key and when does it expire?</summary>

DOKU provides the RSA public key during the Account Billing SFTP setup process. The key must be renewed **annually or bi-annually** — your DOKU Account Manager will notify you ahead of expiry. If you upload a file encrypted with an expired key, DOKU cannot decrypt it and the file will be placed in `/upload/failed`. Do not wait for expiry to request a renewal — rotate proactively as part of your annual security review.

</details>

<details>

<summary>Can I implement the encryption in a language other than Java?</summary>

Yes. The encryption algorithm is standard AES-256 + RSA No Padding — any language with support for these algorithms can implement it. The DOKU Java utility (`tkn-utility.jar`) is provided for convenience and testing, not as a requirement. Validate your implementation in Sandbox against the Java utility before deploying to Production.

</details>

<details>

<summary>Why is the upload folder called /download and vice versa?</summary>

The folder names are from DOKU's perspective. DOKU *downloads* (reads) files from `/download` — so that is where you upload your billing files. DOKU *uploads* (writes) result reports to `/upload` — so that is where you download your reports. As a merchant: write to `/download`, read from `/upload`.

</details>

<details>

<summary>What should I do if my server IP changes?</summary>

Contact your DOKU Account Manager to whitelist the new IP **before** the change takes effect. SFTP connections from non-whitelisted IPs are refused. If you have a dynamic IP, request that DOKU whitelist a static IP range or use a fixed NAT gateway for outbound SFTP connections.

</details>

<details>

<summary>What does RESPONSE CODE 00 mean in the result report?</summary>

`RESPONSE CODE: 00` indicates a successful charge. Any other code indicates a failure. The `RESPONSE MESSAGE` column provides a human-readable description of the failure reason. Contact your DOKU Account Manager for the full response code reference applicable to your payment channels.

</details>

<details>

<summary>How do I parse PAYMENT DATE TIME in the report?</summary>

`PAYMENT DATE TIME` uses the format `yyyyMMddHHmmss` — a 14-digit string with no separators. For example, `20251005121030` = 5 October 2025 at 12:10:30 WIB. Parse by position: characters 1–4 = year, 5–6 = month, 7–8 = day, 9–10 = hour, 11–12 = minute, 13–14 = second.

</details>

<details>

<summary>Do I need to decrypt the result report files before reading them?</summary>

No. Result reports placed by DOKU in the `/upload` folder are plaintext `.TXT` files — they do not require decryption. Only inbound billing files that you upload to `/download` need to be encrypted.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.doku.com/subscription-and-billing/flexibill/account-billing/connectivity-and-file-security-sftp.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
