> 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/merchant-hosted-scheduler.md).

# Merchant Hosted Scheduler

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

**Merchant Hosted Scheduler** gives merchants full control over billing execution. The merchant manages customer payment tokens internally and triggers charges on demand by uploading a batch billing file to DOKU via SFTP. DOKU executes the charges and returns a consolidated result report.

Unlike DOKU Hosted Scheduler where DOKU manages the full schedule automatically, in this model **the merchant owns the schedule** — DOKU only executes the charge when the merchant submits a file.

***

## Features & Benefits

**🎛️ Full Scheduling Control**

Merchants define exactly when charges are executed. Billing is triggered by file upload — not managed automatically by DOKU.

**🗄️ Merchant-Managed Tokens**

Customer payment tokens are obtained and stored by the merchant from a prior first payment. Tokens are reusable across billing cycles as long as the underlying payment method remains valid.

**📁 Bulk Billing via SFTP**

Process large volumes of charges simultaneously by uploading a structured `.TXT` billing file to the DOKU SFTP server. Each file is processed as a batch — one file can cover thousands of customers in a single operation.

**📊 Consolidated Reporting**

After each batch is processed, DOKU generates a result report with per-record outcomes — success, failure, and failure reason — placed in the SFTP outbound directory for merchant download and reconciliation.

***

## Prerequisites

Before using Merchant Hosted Scheduler, the following must be in place:

* [ ] Account Billing activated with a compatible payment channel — [Activate Account Billing](/subscription-and-billing/flexibill/account-billing/account-billing-activation.md)
* [ ] SFTP access configured and IP whitelisted — [Connectivity & File Security](/subscription-and-billing/flexibill/account-billing/connectivity-and-file-security-sftp.md)
* [ ] Customer tokens obtained via First Payment — see [Step 1](#step-1--first-payment--obtain-customer-token) below

***

## How It Works

```
Customer                Merchant System              DOKU
    │                         │                        │
    │── First Payment ────────▶│                        │
    │   (card/direct debit)   │                        │
    │                         │── Get Token List API ──▶│
    │                         │◀─ Token returned ───────│
    │                         │                        │
    │   [On billing date]     │                        │
    │                         │── Prepare .TXT file    │
    │                         │── Encrypt file         │
    │                         │── Upload to SFTP ──────▶│ /download folder
    │                         │── Trigger API ─────────▶│ (optional)
    │                         │                        │
    │                         │                        │── Process batch
    │                         │                        │── Execute charges
    │                         │                        │── Generate report
    │                         │                        │
    │                         │◀─ DOKU notification ───│ (report ready)
    │                         │◀─ Report file ──────────│ /upload folder
    │                         │                        │
    │◀─ Payment notification ─│                        │
```

***

## Step-by-Step Guide

{% stepper %}
{% step %}

### First Payment: Obtain Customer Token

Before a customer can be included in any batch billing file, a **token** must be obtained for their payment method. A token is a secure reference to the customer's payment credential — it never exposes the raw card or bank account number.

**How to get a token:**

1. The customer completes a **First Payment** through one of the supported payment methods:
   * Credit Card / Debit Card → [Direct API - Cards](https://developers.doku.com/accept-payments/direct-api/non-snap/cards)
   * Direct Debit (BRI, MANDIRI) → [Direct API - Direct Debit](https://developers.doku.com/accept-payments/direct-api/snap/integration-guide/direct-debit/bri-direct-debit)
2. After the First Payment completes, call the **Get Token List API** to retrieve the token bound to that customer: → [Get Token List API](https://developers.doku.com/archive/non-snap/tokenization-v1)
3. Store the token in your system alongside the customer record. The token is bound to both the payment credential and the `customer_id` used during the First Payment.

**Token lifecycle:**

| Scenario                                | Token Status                          | Action Required                                                |
| --------------------------------------- | ------------------------------------- | -------------------------------------------------------------- |
| Payment method is active                | ✅ Valid — reuse across billing cycles | No action                                                      |
| Card expired                            | ❌ Invalid — token no longer usable    | Initiate new First Payment with customer to obtain a new token |
| Card cancelled or blocked               | ❌ Invalid                             | Initiate new First Payment with customer                       |
| Charge failed due to insufficient funds | ✅ Still valid                         | Retry with the same token in the next billing cycle            |

{% hint style="info" %}
Tokens are **reusable**. You do not need a new token for each billing cycle — the same token works for every charge against the same payment method until that method becomes invalid.
{% endhint %}

{% hint style="warning" %}
When a card expires and the merchant obtains a new token from the customer's replacement card, use the **new token** in all subsequent batch files. Do not continue submitting the expired token — it will generate a record-level failure every cycle.
{% endhint %}
{% endstep %}

{% step %}

### Prepare the Batch Billing File

Create the billing file in `.TXT` format. Each line in the file represents one charge record.

**File Naming Convention**

The filename **must** include the prefix `TKN_`. Files without this prefix will not be recognized and processed by DOKU.

```
✅ TKN_202510ABC.TXT
✅ TKN_20251005_BATCH01.TXT
❌ BATCH_202510.TXT        (missing TKN_ prefix)
❌ TKN_202510ABC.csv       (wrong extension)
```

{% hint style="warning" %}
File names are used as idempotency keys in the Trigger API. Do not reuse the same filename across different billing cycles — each batch file must have a unique name.
{% endhint %}

**Column Definition**

<table><thead><tr><th width="167.15625">Column</th><th width="133.796875">Type</th><th width="108.06640625">Required</th><th width="94.890625">Length</th><th>Description</th></tr></thead><tbody><tr><td><code>BILLING REF / BILLING NUMBER</code></td><td>Alphanumeric</td><td>✅</td><td>128</td><td>Merchant's unique ID for this billing service record</td></tr><tr><td><code>DESCRIPTION</code></td><td>Alphanumeric</td><td>Optional</td><td>256</td><td>Billing description</td></tr><tr><td><code>INVOICE NUMBER</code></td><td>Alphanumeric</td><td>✅</td><td>64</td><td>Unique invoice number for this charge</td></tr><tr><td><code>CURRENCY</code></td><td>Alpha</td><td>✅</td><td>3</td><td><code>IDR</code> only</td></tr><tr><td><code>AMOUNT</code></td><td>Number</td><td>✅</td><td>10,2</td><td>Charge amount. No decimal: <code>10000</code>. With decimal: <code>10000.00</code></td></tr><tr><td><code>TOKEN</code></td><td>Number</td><td>✅</td><td>32</td><td>Customer payment token obtained from Get Token List API</td></tr><tr><td><code>CARD HOLDER NAME</code></td><td>Alphanumeric</td><td>Optional</td><td>128</td><td>Customer name</td></tr><tr><td><code>CARD HOLDER EMAIL</code></td><td>Alphanumeric</td><td>Optional</td><td>128</td><td>Customer email</td></tr><tr><td><code>CARD HOLDER PHONE</code></td><td>Number</td><td>Optional</td><td>32</td><td>Customer phone number</td></tr><tr><td><code>CARD HOLDER CITY</code></td><td>Alphanumeric</td><td>Optional</td><td>128</td><td>Customer city</td></tr><tr><td><code>CARD HOLDER REGION</code></td><td>Alphanumeric</td><td>Optional</td><td>128</td><td>Customer region/province</td></tr><tr><td><code>CARD HOLDER COUNTRY</code></td><td>Alpha</td><td>Optional</td><td>2</td><td>ISO country code — e.g., <code>ID</code></td></tr><tr><td><code>CARD HOLDER ADDRESS</code></td><td>Alphanumeric</td><td>Optional</td><td>128</td><td>Customer street address</td></tr><tr><td><code>CARD HOLDER POSTAL CODE</code></td><td>Alphanumeric</td><td>Optional</td><td>16</td><td>Customer postal code</td></tr><tr><td><code>CARD HOLDER BIRTHDATE</code></td><td>Number</td><td>Optional</td><td>8</td><td>Customer birthdate in <code>yyyyMMdd</code> format</td></tr><tr><td><code>EXECUTE DATE</code></td><td>Number</td><td>Optional</td><td>8</td><td>Specific date to execute this charge in <code>YYYYMMDD</code> format. If omitted, DOKU executes the record immediately when the file is processed. Must not be a past date.</td></tr></tbody></table>

{% hint style="warning" %}
`EXECUTE DATE` must not be a date in the past. If a past date is provided, the record will fail with an `INVALID EXECUTE DATE` error and the file will be placed in `/upload/failed` without any charges being executed. Set `EXECUTE DATE` to today or a future date, or omit it to execute immediately.
{% endhint %}
{% endstep %}

{% step %}

### Encrypt and Upload to SFTP

Before uploading, the file must be encrypted. After encryption, upload to the DOKU SFTP `/download` folder.

For full encryption procedure, folder structure, and SFTP connection details → [Connectivity & File Security (SFTP)](broken://pages/e9ccc3696e23f5f887d028467307ac8ed2b6fcdb)
{% endstep %}

{% step %}

### Trigger DOKU Processing *(Optional)*

After uploading the file, optionally call the **Trigger Batch API** to initiate processing immediately. Without this call, DOKU's internal scheduler will automatically pick up and process the file at its next polling interval.

**Endpoint:**

<table><thead><tr><th width="98.7734375"></th><th width="294">Sandbox</th><th>Production</th></tr></thead><tbody><tr><td><strong>Method</strong></td><td><code>POST</code></td><td><code>POST</code></td></tr><tr><td><strong>URL</strong></td><td><code>https://api-sandbox.doku.com/batch-upload/v1/notify</code></td><td><code>https://api.doku.com/batch-upload/v1/notify</code></td></tr></tbody></table>

**Request Headers:**

<table><thead><tr><th width="179.19140625">Header</th><th>Description</th></tr></thead><tbody><tr><td><code>Client-Id</code></td><td>Client ID from DOKU Back Office</td></tr><tr><td><code>Request-Id</code></td><td>Unique random string, max 128 characters</td></tr><tr><td><code>Request-Timestamp</code></td><td>ISO 8601 UTC+0 format. For WIB (UTC+7), subtract 7 hours — e.g., 08:51 WIB = <code>2020-09-22T01:51:00Z</code></td></tr><tr><td><code>Signature</code></td><td>HMAC-SHA256 signature generated on merchant backend</td></tr></tbody></table>

**Request Body:**

```json
{
  "file_name": "TKN_202510ABC.TXT"
}
```

<table><thead><tr><th width="119.578125">Parameter</th><th width="86.3671875">Type</th><th width="106.08984375">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>file_name</code></td><td>String</td><td>✅</td><td>Exact filename as uploaded to the SFTP <code>/download</code> folder, including the <code>TKN_</code> prefix and <code>.TXT</code> extension</td></tr></tbody></table>

**Response Scenarios:**

{% tabs %}
{% tab title="✅ In Process" %}
**HTTP 201 — File accepted for processing.**

```json
{
  "name": "TKN_202510ABC.TXT",
  "status": "IN_PROCESS"
}
```

DOKU has received the trigger and is processing the file. Monitor the SFTP `/upload` folder for the result report and wait for the DOKU notification callback.
{% endtab %}

{% tab title="❌ Invalid Signature" %}
**HTTP 400 — Signature validation failed.**

```json
{
  "error": {
    "code": "invalid_signature",
    "message": "invalid header signature",
    "type": "Invalid Signature"
  }
}
```

**Resolution:** Regenerate the `Signature` header using the correct HMAC-SHA256 algorithm and your `Client-Secret`. Verify `Request-Timestamp` is in ISO 8601 UTC+0 and within acceptable clock skew.
{% endtab %}

{% tab title="❌ Invalid Parameter" %}
**HTTP 400 — `file_name` is missing or empty.**

```json
{
  "error": {
    "code": "invalid_parameter",
    "message": "file_name must not be empty",
    "type": "Not input object file_name"
  }
}
```

**Resolution:** Include the `file_name` field in the request body with the exact filename of the uploaded file.
{% endtab %}

{% tab title="❌ Duplicate File Name" %}
**HTTP 400 — `file_name` has already been submitted.**

```json
{
  "error": {
    "code": "idempotent_request",
    "message": "idempotent request",
    "type": "Duplicate file name"
  }
}
```

**Resolution:** This filename has already been triggered for processing. Do not resubmit — the file is either already processing or has completed. Check the SFTP `/upload` folder for the result report. If you need to submit a new billing batch, use a different filename.
{% endtab %}
{% endtabs %}
{% endstep %}

{% step %}

### Receive DOKU Processing Notification

When DOKU finishes processing the batch file, it sends a notification to your configured merchant endpoint. This is the signal that the result report is ready to download.

**DOKU sends a `POST` request to your notification URL with:**

```json
{
  "service": {
    "id": "BATCH_UPLOAD"
  },
  "batch_file": {
    "name": "TKN_202510ABC.TXT",
    "status": "DONE",
    "date": "2025-10-05T13:52:53Z"
  }
}
```

| Field               | Description                                                                              |
| ------------------- | ---------------------------------------------------------------------------------------- |
| `service.id`        | Always `BATCH_UPLOAD` for batch processing notifications                                 |
| `batch_file.name`   | The filename that was processed                                                          |
| `batch_file.status` | `DONE` — processing is complete. The result report is ready in the SFTP `/upload` folder |
| `batch_file.date`   | Timestamp when processing completed, ISO 8601 UTC+0                                      |

{% hint style="info" %}
Configure your notification endpoint URL under **Settings → Account Billing → Bill Detail Setting → URL Notification**.
{% endhint %}
{% endstep %}

{% step %}

### Download and Reconcile the Report

After receiving the DOKU notification, download the result report from the SFTP `/upload` folder.

For the full report file structure, field definitions, and BANK CODE lookup table → [Connectivity & File Security — Report File](broken://pages/e9ccc3696e23f5f887d028467307ac8ed2b6fcdb#report-file-format)

**Reconciliation triage — how to handle FAILED records:**

<table><thead><tr><th width="207.4765625">Failure Type</th><th>Signal in Report</th><th>Action</th></tr></thead><tbody><tr><td><strong>Insufficient funds</strong></td><td><code>FAILED</code> + bank declined response code</td><td>Token is still valid — retry the customer in the next billing cycle file</td></tr><tr><td><strong>Expired or invalid token</strong></td><td><code>FAILED</code> + token-related response code</td><td>Token is no longer usable — initiate a new First Payment with the customer to obtain a new token, then use the new token in the next cycle</td></tr><tr><td><strong>Card blocked / stolen</strong></td><td><code>FAILED</code> + bank restriction response code</td><td>Contact customer for a new payment method — initiate new First Payment once resolved</td></tr><tr><td><strong>Invalid execute date</strong></td><td><code>FAILED</code> + <code>INVALID EXECUTE DATE</code></td><td>Correct the <code>EXECUTE DATE</code> in your next file — must not be a past date</td></tr><tr><td><strong>Already paid</strong></td><td><code>ALREADY_PAID</code></td><td>Send One Bill/Month is active and the customer was already charged this month — no action needed</td></tr><tr><td><strong>Decryption failure</strong></td><td>File in <code>/upload/failed</code>, no per-record report</td><td>Re-encrypt with the correct DOKU public key and re-upload with a new filename</td></tr></tbody></table>
{% endstep %}
{% endstepper %}

***

## Use Cases

{% tabs %}
{% tab title="Insurance" %}

<table><thead><tr><th width="151.12890625"></th><th>Details</th></tr></thead><tbody><tr><td><strong>Description</strong></td><td>An insurance company collects quarterly premium payments from thousands of policyholders on the same date. Their policy management system manages all customer data and tokens internally.</td></tr><tr><td><strong>Solution</strong></td><td>On each quarterly billing date, the policy system generates a <code>.TXT</code> batch file with all active policyholder tokens and uploads to DOKU SFTP. DOKU processes all charges in one operation.</td></tr><tr><td><strong>How It Works</strong></td><td>System generates billing file → encrypt → upload to SFTP <code>/download</code> → Trigger API (optional) → DOKU processes charges → report in <code>/upload</code> → merchant downloads and reconciles</td></tr><tr><td><strong>Features Used</strong></td><td>Merchant Hosted Scheduler, SFTP Batch Processing, Consolidated Report</td></tr></tbody></table>
{% endtab %}

{% tab title="Utility" %}

<table><thead><tr><th width="156.9296875"></th><th>Details</th></tr></thead><tbody><tr><td><strong>Description</strong></td><td>A utility company bills customers on the 5th of each month with amounts that vary based on metered usage.</td></tr><tr><td><strong>Solution</strong></td><td>Each month, the billing system calculates per-customer usage, builds a <code>.TXT</code> file with dynamic <code>AMOUNT</code> per customer and <code>EXECUTE DATE: 5th</code>, and uploads via SFTP.</td></tr><tr><td><strong>How It Works</strong></td><td>Billing system calculates usage → builds file with per-record amounts → encrypt → upload → DOKU charges each customer at their calculated amount → report returned</td></tr><tr><td><strong>Features Used</strong></td><td>Merchant Hosted Scheduler, Dynamic Amount per Record, EXECUTE DATE</td></tr></tbody></table>
{% endtab %}
{% endtabs %}

***

## Merchant & Customer Experience

#### Merchant View

* Manage customer tokens internally — obtain via First Payment, store per customer record
* Prepare and upload batch `.TXT` billing file to SFTP `/download` folder on each billing cycle
* Optionally call Trigger API to initiate immediate processing
* Receive DOKU notification when report is ready
* Download result report from SFTP `/upload` folder
* Triage failed records: retry with same token (insufficient funds), or initiate new First Payment (expired/invalid token)

#### Customer View

* Completes a First Payment once during onboarding to authorize recurring charges
* Receives a payment success or failure notification after each charge attempt
* If card expires: goes through checkout again for a new card — merchant handles the rest

***

## Terms & Conditions

* Merchants are responsible for securely storing and managing customer tokens
* Batch files must use `TKN_` prefix in filename and `.TXT` extension — files not matching this convention will not be processed
* `EXECUTE DATE` must not be a past date — past dates generate per-record failures placed in `/upload/failed`
* SFTP access requires IP whitelisting — [Connectivity & File Security](broken://pages/e9ccc3696e23f5f887d028467307ac8ed2b6fcdb)
* Raw card numbers must never appear in batch files — use DOKU-issued tokens only

***

## FAQ

<details>

<summary>What is a token and how do I get one?</summary>

A token is a secure reference to a customer's payment credential — it represents their card or bank account without exposing the raw numbers. Tokens are obtained by having the customer complete a First Payment through the supported payment methods (Credit Card, Direct Debit BRI, or E-Wallet OVO). After the First Payment completes, call the Get Token List API to retrieve the token bound to that customer ID. Store the token in your system — it is reusable for all future billing cycles against the same payment method.

</details>

<details>

<summary>Can I reuse the same token across multiple billing cycles?</summary>

Yes. A token is persistent and remains valid as long as the underlying payment method is active. You do not need a new token each cycle — include the same token in every batch file until the card expires, is cancelled, or the customer's payment method changes.

</details>

<details>

<summary>What happens if a customer's card expires?</summary>

The token bound to the expired card becomes invalid. The charge will fail with a token-related error in the batch report. You must initiate a new First Payment with the customer so they can register their replacement card — then call Get Token List API to obtain the new token and use it in subsequent batch files. Do not continue submitting the expired token — it will fail every cycle until replaced.

</details>

<details>

<summary>Can I retry a failed charge with the same token after an insufficient funds failure?</summary>

Yes. An insufficient funds failure does not invalidate the token — the card itself is still active, it simply lacked balance at the time of the charge attempt. You can include the same token in your next billing cycle's batch file for a retry.

</details>

<details>

<summary>What happens if I omit EXECUTE DATE from a record?</summary>

The record is executed immediately when DOKU processes the batch file. If you need charges to run on a specific future date, set `EXECUTE DATE` to that date in `YYYYMMDD` format. If you omit it entirely, the charge runs as soon as the file is processed.

</details>

<details>

<summary>What happens if EXECUTE DATE is set to a past date?</summary>

The file is placed in the SFTP `/upload/failed` folder and DOKU sends a failure notification to your endpoint. No charges are executed for any record in the file. Correct the `EXECUTE DATE` to today or a future date, re-encrypt, and re-upload with a **new filename**.

</details>

<details>

<summary>What happens if some records in my batch file fail but others succeed?</summary>

Record-level failures (insufficient funds, invalid token, etc.) do not affect other records in the same file. Valid records are charged; failed records are flagged in the result report with their specific error reason. Review the report to triage each failure and take the appropriate action per record.

</details>

<details>

<summary>If I don't call the Trigger API, when will DOKU process my file?</summary>

DOKU has an internal scheduler that automatically polls the SFTP `/download` folder at pre-defined intervals. Your file will be processed even without the Trigger API call — the trigger only allows you to initiate processing immediately rather than waiting for the next polling interval. Contact your DOKU Account Manager for the specific polling interval applicable to your account.

</details>

<details>

<summary>What does the duplicate file name error on the Trigger API mean?</summary>

The Trigger API uses the `file_name` as an idempotency key — the same filename cannot be submitted twice. If you receive this error, the file has already been triggered for processing. Do not resubmit. Check the SFTP `/upload` folder for the result report. For a new billing batch, always use a unique filename.

</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/merchant-hosted-scheduler.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.
