How ACH works: A developer perspective - Part 4

At Gusto, we rely heavily on the ACH network to pay employees and to remit various payroll taxes to federal and state agencies on behalf of our clients. We even use the ACH network for remitting employee-initiated donations to non-profits from their paycheck.

In part 1 of this post, I outlined the basics of how we originate ACH debits and credits. In part 2 of this post, I went into the details of how ACH returns are handled. Finally, in part 3 of this post, I explained the timing of these ACH transfers. Part 4 of this post will go into the actual ACH file format.

An ACH file is nothing more than a file with multiple lines of ASCII text, each line 94 characters in length. A line is a called a "record" in ACH parlance.

There are 5 main record types in an ACH file:

1) File Header Record
2) Batch Header Record
3) PPD Entry Detail Record
4) Batch Control Record
5) File Control Record

An ACH file can contain one or more batches and a batch can contain one or more entries. I'll describe what batches and entries are in their sections below.

Here's a visual format of how the 5 records are placed in relation to each other.

ACH Record Layout

As you can tell, a File Hearder Record must always be closed by a File Control Record, and a File Batch Record must always be closed by the Batch Control Record. Think of them as HTML tags.

Before getting into the nitty-gritty of the file contents, there are a couple ground rules:

  • If the contents in a field is alphanumeric and does not take up all the space allocated for the field, the contents should be post-padded with spaces until the entire allocated space is used up.
  • If the contents in a field is numeric and does not take up all the space allocated for the field, the contents should be pre-padded with zeros until the entire allocated space is used up.

File Header Record

There is always exactly one File Header Record in each ACH file, and it's always the first record in the file. The File Header Record contains high-level information about the contents of the ACH file.

Character Positions Field Name Description
1-1 Record type code Always "1"
2-3 Priority code Always "01"
4-13 Immediate destination A blank space followed by your ODFI's routing number. For example: " 121140399"
14-23 Immediate origin A 10-digit number assigned to you by the ODFI once they approve you to originate ACH files through them
24-29 File creation date Today's date in YYMMDD format
30-33 File creation time The current time in HHMM format
34-34 File ID modifier Always "A"
35-37 Record size Always "094"
38-39 Blocking factor Always "10"
40-40 Format code Always "1"
41-63 Immediate destination name The name of the ODFI. For example: "SILICON VALLEY BANK    "
64-86 Immediate origin name Your company's name.
87-94 Reference code Optional field you may use to describe the ACH file for internal accounting purposes

Batch Header Record

The batch record defines certain shared characteristics of each of the Entry Detail Records inside it. For example, the Batch Header Record will instruct the bank whether the entries inside it are credit or debits. They will also tell the RDFI what text to put on the receiver's bank statement for the credit or debit.

Character Positions Field Name Description
1-1 Record type code Always "5"
2-4 Service code If the entries are credits, always "220". If the entries are debits, always "225"
5-20 Company name Your company's name. This name may appear on the receivers’ statements prepared by the RDFI.
21-40 Company discretionary data Optional field you may use to describe the batch for internal accounting purposes
41-50 Company identification A 10-digit number assigned to you by the ODFI once they approve you to originate ACH files through them. This is the same as the "Immediate origin" field in File Header Record
51-53 Standard entry class code If the entries are PPD (credits/debits towards consumer account), use "PPD". If the entries are CCD (credits/debits towards corporate account), use "CCD". The difference between the 2 class codes are outside of the scope of this post, but generally most ACH transfers to consumer bank accounts should use "PPD"
54-63 Company entry description Your description of the transaction. This text will appear on the receivers’ bank statement. For example: "Payroll   "
64-69 Company descriptive date The date you choose to identify the transactions in YYMMDD format. This date may be printed on the receivers’ bank statement by the RDFI
70-75 Effective entry date Date transactions are to be posted to the receivers’ account. You almost always want the transaction to post as soon as possible, so put tomorrow's date in YYMMDD format
76-78 Settlement date Always blank (just fill with spaces)
79-79 Originator status code Always "1"
80-87 ODFI identification Your ODFI's routing number without the last digit. The last digit is simply a checksum digit, which is why it is not necessary
88-94 Batch number Sequential number of this Batch Header Record. For example, put "1" if this is the first Batch Header Record in the file

PPD Entry Detail Record

This record contains most of the important information related to the account we're crediting/debiting and the amount of the transfer.

Character Positions Field Name Description
1-1 Record type code Always "6"
2-3 Transaction code If the receiver's account is:
Credit to checking account: "22"
Debit to checking account: "27"
Credit to savings account: "32"
Debit to savings account: "37"
4-11 RDFI identification The RDFI's routing number without the last digit.
12-12 Check digit The last digit of the RDFI's routing number
13-29 DFI account number The receiver's bank account number you are crediting/debiting. It important to note that this is an alphanumeric field, so its space padded, no zero padded
30-39 Amount Number of cents you are debiting/crediting this account
40-54 Individual identification number An internal identification (alphanumeric) that you use to uniquely identify this Entry Detail Record
55-76 Individual name The name of the receiver, usually the name on the bank account
77-78 Discretionary data Always blank (just fill with spaces)
79-79 Addenda record indicator Always "0"
80-94 Trace number An internal identification (alphanumeric) that you use to uniquely identify this Entry Detail Record. This number should be unique to the transaction and will help identify the transaction in case of an inquiry

Batch Control Record

This record marks the end of the previous batch. There is always one Batch Control Record for each Batch Header Record. The Batch Control Record contains various checksums to ensure that you've generated the batch correctly and no information was lost during the transmission of the file.

Character Positions Field Name Description
1-1 Record type code Always "8"
2-4 Service code This is the same as the "Service code" field in previous Batch Header Record
5-10 Entry count Total number of Entry Detail Record in the batch
11-20 Entry hash Total of all positions 4-11 on each Entry Detail Record in the batch. This is essentially the sum of all the RDFI routing numbers in the batch. If the sum exceeds 10 digits (because you have lots of Entry Detail Records), lop off the most significant digits of the sum until there are only 10
21-32 Total debit entry dollar amount Number of cents of debit entries within the batch
33-44 Total credit entry dollar amount Number of cents of credit entries within the batch
45-54 Company identification This is the same as the "Company identification" field in previous Batch Header Record
55-73 Message authentication code Always blank (just fill with spaces)
74-79 Reserved Always blank (just fill with spaces)
80-87 ODFI identification This is the same as the "ODFI identification" field in previous Batch Header Record
88-94 ODFI identification This is the same as the "Batch number" field in previous Batch Header Record

File Control Record

There is always exactly one File Control Record in each ACH file, and it's always the last record in the file. The File Control Record contains various checksums to ensure that you've generated the file correctly and no information was lost during the transmission of the file.

Character Positions Field Name Description
1-1 Record type code Always "9"
2-7 Batch count The total number of Batch Header Record in the file. For example: "000003"
8-13 Block count The total number of blocks on the file, including the File Header and File Control records. One block is 10 lines, so it's effectively the number of lines in the file divided by 10.
14-21 Entry count Total number of Entry Detail Record in the file
22-31 Entry hash Total of all positions 4-11 on each Entry Detail Record in the file. This is essentially the sum of all the RDFI routing numbers in the file. If the sum exceeds 10 digits (because you have lots of Entry Detail Records), lop off the most significant digits of the sum until there are only 10
32-43 Total debit entry dollar amount in file Number of cents of debit entries within the file
44-55 Total credit entry dollar amount in file Number of cents of credit entries within the file
56-94 Reserved Always blank (just fill with spaces)

Because it can be tedious and error prone to generate large fixed width documents like ACH files, we've created and open sourced Fixy, a tool for generated fixed width documents in Ruby. You can read more about this library here. Before RESTful APIs were all the rage, it seems that fixed width files were a popular way to pass information from one system to another. Many legacy systems, not just ACH, use fixed width files.

Parts 1, 2, 3, and 4 of this series should give a pretty good overview of the ACH protocol and its implementation at a high level. For the sake of clarity, much has been simplified. In reality, there are many more record types that I did not cover. The offical NACHA rulebook, where you can find the complete specification of the ACH files, is 479 pages long!

Today, Gusto moves billions of dollars annually through the ACH network, and it's a critical part of how we pay employees for their work and the government for their taxes. If problems of this nature look interesting to you, we're hiring talented engineers.

This concludes my blog series on ACH, but I'd be happy to cover other parts of the ACH protocol in more detail if there's interest in the comments!

Comments on Hacker News