---
source: https://carbone.io/examples/nacha-ach-file/
title: "NACHA File Generator: Create ACH Files from JSON | Carbone"
description: "Create NACHA files from your payroll or supplier payment data with the Carbone API: a TXT template writes the ACH file, entry hash and totals included."
generated_at: "2026-11-12"
---

# NACHA File Generator: Create ACH Files from JSON

Turn payroll or supplier payments from your software into a valid ACH file

[![NACHA File Generator: Create ACH Files from JSON](/examples/nacha-ach-file/thumb-result.webp)](/examples/nacha-ach-file/result.txt)

[Download Template](/examples/nacha-ach-file/template.txt)

[Download data sample](/examples/nacha-ach-file/data.json)

-   Template type document txt simple
-   Carbone min. v5.4.3+
-   Features loop aggregator padding condition
-   Target finance payroll bank

## Generate NACHA files from your software

Payroll platforms, accounting software and fintechs pay through US banks with NACHA files, also called ACH files: text files of 94-character records in blocks of 10 lines, in the format set by [Nacha](https://www.nacha.org/). Your application sends its payments to the Carbone API as JSON and gets back the ACH file to upload to the bank. In this example, Acme Corporation pays five employees.

## NACHA file layout

The first character of each line is its record code:

| Record | Code | Content |
| --- | --- | --- |
| File Header | `1` | Who sends the file, to which bank, and when |
| Batch Header | `5` | The company, the payment type and the pay date |
| Entry Detail | `6` | One line per employee: bank, account, amount in cents, name |
| Batch Control | `8` | Batch totals: payment count, entry hash (a checksum), amount paid |
| File Control | `9` | The same totals for the file, plus the block count |
| Filler lines | `9` | Lines of 94 nines that complete the last block |

## NACHA file example

### Template

```text
{o.timezone=America/New_York}101 {d.bank.routing}{d.company.id}{d.file.createdAt:formatD('YYMMDDHHmm')}A094101{d.bank.name:upperCase:padr(23)}{d.company.name:upperCase:padr(23)}{d.file.reference:padr(8)}
5220{d.company.name:upperCase:substr(0, 16):padr(16)}                    {d.company.id}PPD{d.batch.description:upperCase:padr(10)}{d.batch.effectiveDate:formatD('MMM DD'):upperCase}{d.batch.effectiveDate:formatD('YYMMDD')}   1{d.bank.routing:substr(0, 8)}0000001
{d.entries[i]}6{d.entries[i].accountType:ifEQ(savings):show(32):elseShow(22)}{d.entries[i].routing}{d.entries[i].account:padr(17)}{d.entries[i].amount:mul(100):round(0):padl(10, '0')}{d.entries[i].id:padr(15)}{d.entries[i].name:unaccent:upperCase:substr(0, 22):padr(22)}  0{d.bank.routing:substr(0, 8)}{d.entries[i].id:cumCount:padl(7, '0')}
{d.entries[i+1]}8220{d.entries:len:padl(6, '0')}{d.entries[].routing:substr(0, 8):aggSum:mod(10000000000):padl(10, '0')}000000000000{d.entries[].amount:mul(100):round(0):aggSum:padl(12, '0')}{d.company.id}                         {d.bank.routing:substr(0, 8)}0000001
9000001{d.entries:len:add(4):div(10):ceil:padl(6, '0')}{d.entries:len:padl(8, '0')}{d.entries[].routing:substr(0, 8):aggSum:mod(10000000000):padl(10, '0')}000000000000{d.entries[].amount:mul(100):round(0):aggSum:padl(12, '0'):padr(51)}{d.entries:len:add(3):mod(10):ifLT(9):showBegin}
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999{d.entries:showEnd}{d.entries:len:add(3):mod(10):ifLT(8):showBegin}
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999{d.entries:showEnd}{d.entries:len:add(3):mod(10):ifLT(7):showBegin}
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999{d.entries:showEnd}{d.entries:len:add(3):mod(10):ifLT(6):showBegin}
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999{d.entries:showEnd}{d.entries:len:add(3):mod(10):ifLT(5):showBegin}
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999{d.entries:showEnd}{d.entries:len:add(3):mod(10):ifLT(4):showBegin}
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999{d.entries:showEnd}{d.entries:len:add(3):mod(10):ifLT(3):showBegin}
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999{d.entries:showEnd}{d.entries:len:add(3):mod(10):ifLT(2):showBegin}
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999{d.entries:showEnd}{d.entries:len:add(3):mod(10):ifLT(1):showBegin}
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999{d.entries:showEnd}
```

### JSON data

```json
{
  "file": { "createdAt": "2026-11-12T14:05:00-05:00", "reference": "PR261113" },
  "bank": { "name": "Contoso Bank", "routing": "041108291" },
  "company": { "name": "Acme Corporation", "id": "1470259836" },
  "batch": { "description": "Payroll", "effectiveDate": "2026-11-13" },
  "entries": [
    { "id": "EMP00417", "name": "Olivia Bennett", "accountType": "checking", "routing": "071403623", "account": "3820174956", "amount": 2845.16 },
    { "id": "EMP00422", "name": "José Ramírez", "accountType": "savings", "routing": "263071575", "account": "000738291054", "amount": 1932.5 },
    { "id": "EMP00431", "name": "Hannah Kowalski", "accountType": "checking", "routing": "322374184", "account": "59302817", "amount": 3410 },
    { "id": "EMP00438", "name": "Marcus Thompson", "accountType": "checking", "routing": "062109512", "account": "1100493827", "amount": 2276.84 },
    { "id": "EMP00445", "name": "Priya Raghunathan-Sullivan", "accountType": "savings", "routing": "113017346", "account": "7730015529", "amount": 4105.37 }
  ]
}
```

### Generated ACH file

```text
101 04110829114702598362611121405A094101CONTOSO BANK           ACME CORPORATION       PR261113
5220ACME CORPORATION                    1470259836PPDPAYROLL   NOV 13261113   1041108290000001
6220714036233820174956       0000284516EMP00417       OLIVIA BENNETT          0041108290000001
632263071575000738291054     0000193250EMP00422       JOSE RAMIREZ            0041108290000002
62232237418459302817         0000341000EMP00431       HANNAH KOWALSKI         0041108290000003
6220621095121100493827       0000227684EMP00438       MARCUS THOMPSON         0041108290000004
6321130173467730015529       0000410537EMP00445       PRIYA RAGHUNATHAN-SULL  0041108290000005
822000000500831976220000000000000000014569871470259836                         041108290000001
9000001000001000000050083197622000000000000000001456987                                       
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
```

## How to create a NACHA file

### 1\. Write one line per record

Type the fixed values as they are, like the record size `094`, and build the other fields with the [fixed-width formatters](/documentation/design/template-formats/txt.md#fixed-width-files): `{d.company.name:upperCase:substr(0, 16):padr(16)}` fits ACME CORPORATION into its 16 characters. `{o.timezone=America/New_York}` prints the file time in New York time.

### 2\. Add one Entry Detail per payment

The Entry Detail line is a [loop](/documentation/design/template-formats/txt.md#loops) on `d.entries`. On Olivia Bennett's line:

-   `22` pays a checking account, `32` a savings account: `:ifEQ(savings):show(32):elseShow(22)`.
-   `071403623` is the routing number of her bank.
-   `:mul(100):round(0):padl(10, '0')` turns 2,845.16 into `0000284516`.
-   `{d.entries[i].id:cumCount:padl(7, '0')}` ends the trace number with her position: `041108290000001`.

### 3\. Compute the control totals

The bank recomputes these totals and rejects a file that does not match. Carbone computes them from the payments with [aggregators](/documentation/design/computation/aggregation.md):

-   **Entry count**: `{d.entries:len:padl(6, '0')}` prints `000005`.
-   **Entry hash**: `{d.entries[].routing:substr(0, 8):aggSum:mod(10000000000):padl(10, '0')}` adds the first 8 digits of each routing number and keeps the last 10 digits: `0083197622`.
-   **Total credits**: `{d.entries[].amount:mul(100):round(0):aggSum:padl(12, '0')}` prints `000001456987`, or 14,569.87.

### 4\. Fill the last block with 9s

The line count must be a multiple of 10. `{d.entries:len:add(4):div(10):ceil}` gives the block count, and the template ends with nine lines of `9`. Each one is a [conditional block](/documentation/design/conditions/conditional-blocks.md#showbegin-showend) on `{d.entries:len:add(3):mod(10)}`, the position of the File Control in its block, so only the missing lines are kept: one with 5 payments, nine with 7.

The ACH file keeps the CRLF line endings of the template.

## Related Examples

[![Bank statement (simple)](/examples/bank-statement-simple/thumb-result.webp)](/examples/bank-statement-simple/index.md) [![Create EDIFACT Invoices from JSON with an EDI API](/examples/edifact-invoice/thumb-result.webp)](/examples/edifact-invoice/index.md)

## Trusted by 800+ paid customers in 40+ countries

Industries: Finance, Healthcare, Insurance, Legal, Public, Retail, Energy, Software, Telecom.

Customers include Dior, Thales, Younited, Danfoss, CMA CGM, Illumina, KparK, Scalingo, French Ministry of the Armed Forces, BMW, Telus, Bestseller, Flagship Biosciences, Insly, Ninox, Sodexo.

## Related topics

- [Transport ticket](/examples/ticket/index.md)
- [Summary](/examples/summary/index.md)
- [Stock inventory spreadsheet](/examples/stock-inventory-spreadsheet/index.md)
- [Store Inventory](/examples/shoes/index.md)
- [Sensor readings](/examples/sensor-readings/index.md)
- [Real estate property document](/examples/real-estate-property-description/index.md)
