Your guide to the Moov ACH Gateway

The Moov ACH Gateway is a customizable payment gateway that enables automated ACH operations including file uploading and downloading. When paired with our test harness, which acts as a financial institution, achgateway serves as the final puzzle piece for ACH transactions. It also utilizes Moov ACH by flattening batches, merging files, and validating files to ensure transfers are cost effective and reliable. We’ll cover how to set up the service and run through a sample use case in the sections below.


Think of achgateway as an automated software engine capable of supporting a variety of input/output sources and broadcasting a stream of events for other services to consume. Inspired by our paygate project, achgateway makes several major improvements over paygate such as support for use in a highly available setup and the ability to merge multiple input sources (HTTP, Kafka, etc.) into ACH files for your ODFI. It continues to offer options for encoding and encryption. Additionally, users can divide uploads into groups via shards and point the gateway toward multiple ODFIs. These features provide significant flexibility when building real-world ACH transfer scenarios.

Gateway flowchart

One use case of the gateway is for micro-deposits, where small amounts of money are submitted to an account for validation purposes. The service can be configured to upload a group of Same-Day ACH micro-deposit transfers at the end of day, optimizing cost and performance. Lastly, notifications can be triggered by successful uploads or errors. We currently support Slack, email, and PagerDuty for these alerts.

Initial setup

  • To get started with a default instance of achgateway, first install Docker on your system and clone the project.

  • From the ./examples/getting-started/ directory, run docker compose up. This step may take a minute to complete and will run the latest image on port 8484 with Prometheus metrics on port 9494.

  • Based on the sample configuration being used, the ach-test-harness will save files into ./testdata/ftp-server/ after achgateway uploads them. There, you will find some ACH files to experiment with.


achgateway supports listening over TLS for inbound files in v0.6 and later. To enable this, simply set the TLS config under the Inbound section.

    BindAddress: “:8484”
      CertFile: “/path/to/certificate.pem”
      KeyFile: “/path/to/private.key”

Submitting files

When individual files are submitted to achgateway for upload, they are loaded into a queue. Incoming files are assigned a shard key, which serves as a unique identifier and maps to a shard name. Shards tell the gateway when and where to send files by specifying upload times (cutoff windows) and an uploadAgent. Assigning multiple keys to the same name enables grouped uploads, each with their own customizable behavior.

Below, you’ll find how shards are defined in a basic config.yml file:

      - name: "testing"
          timezone: "America/Los_Angeles"
            - "10:30"
            - "14:00"
        uploadAgent: "local-ftp"
          format: "nacha"
      "foo": "testing"

Notice how the shard key foo is mapped to testing and will upload to local-ftp. Try queueing up a file with:

curl -XPOST "localhost:8484/shards/foo/files/f4" --data @./testdata/ppd-valid.json

Uploading files

Cutoff times trigger queued files to upload. In production, these would match Nacha’s ACH submission deadlines to optimize uploads. Rather than wait for a cutoff to occur in real time, manually trigger one with:

curl -XPUT "localhost:9494/trigger-cutoff"

Observe how the file we submitted earlier appears in the local FTP server’s filesystem under the outbound directory. Files are validated, flattened (optionally), and merged before upload to reduce costs as much as possible. Outbound ACH file names are built from a template like YYYYMMDD-HHMMSS.ach, which can be defined in the gateway configuration. In our example, the uploaded file name will be set to something like 20210719-170711.ach. Note that time is formatted as a 24-hour clock in GMT.

Downloading files

Connected ODFIs may generate certain files like returns, corrections, and pre-notifications. The achgateway can periodically check for these files and download them into a filesystem location on a scheduled interval. In our example, files in the FTP server’s reconciliation and returned folders are eligible for download. You can manually trigger inbound file processing with:

curl -XPUT "localhost:9494/trigger-inbound"

Notice how ppd-debit.ach and return-web.ach will be removed from the FTP server.

High availability

achgateway is designed to be run in coordination with multiple identical instances. This allows for resilience in guaranteeing file uploads and sharing the load across multiple instances. To enable this, point achgateway toward a healthy Consul cluster so it can elect leadership for each shard just prior to upload.

  Address: “”
  Scheme: “http”
  SessionPath: “achgateway/shards/”

Each instance of achgateway accepts all files to prepare for merging and uploading later on. At a cutoff time, each instance will prepare the merged files and attempt to become the leader. When an instance obtains leadership, it implies that it’s the sole leader and can upload the merged files.

Non-leader instances will skip uploading files in the current batch and continue onto the next batch of files to upload. In the future, we plan to support accepting/rejecting specific shard keys or handling only a fraction of encountered keys in each instance. Both of these options are strategies to help achgateway scale toward millions of entries processed during a cutoff time.


To explore additional achgateway features like audit trails, file encryption, notifications, and error alerts, we recommend checking out our project documentation and full configuration specification. We hope this walkthrough helps you get started with the gateway and serves as a quick refresher for current users. If you have any questions or suggestions for the project, please contact the Moov team on Slack (#ach channel) or submit an issue.