Free tools / SEPA & NACHA Payment File API
Convert a CSV of payments to a NACHA (ACH) file
Turn a list of payments into a NACHA file with nothing left to chance. The first tab shows a file the API made from a three-line CSV; drop a NACHA file below to check one you already have. The conversion itself is an API call.
- Free, no sign-up
- Your file is never stored
- Deterministic: no AI guesswork
Try it now
The demo validates NACHA files; turning a CSV into one is an API call. The first sample was generated from the CSV shown below. Please do not upload a real file with real account numbers.
The live demo needs JavaScript, which seems to be switched off. The sample tabs show the engine's real output without it.
Free: 5 runs a day, no sign-up. Files that cannot be read do not count.
A PPD payroll file (three credits) generated by the API from a three-line CSV. Every check passes. Names, accounts and routing numbers are made up.
ach-ppd-payroll.ach 950 bytes · NACHA PPD
Input file
101 07400001012345678902610210900A094101SAMPLE BANK ACME PAYROLL INC
5220ACME PAYROLL 1234567890PPDPAYROLL 261023 1074000010000001
6221210003584400123456 0000185000 MARIA LOPEZ 1074000010000001
705OCTOBER SALARY 00010000001
63202200006277-1290 0000231045 SAM O'BRIEN 1074000010000002
705OCTOBER SALARY 00010000002
6220610001599100022 0000167520 LEE CHEN 0074000010000003
822000000500204000560000000000000000005835651234567890 074000010000001
9000001000001000000050020400056000000000000000000583565
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
Validation result
VALID 0 errors · 0 warnings
- ACME PAYROLL INC
- 2026-10-21
- Batches · entries
- 1 · 3
- Total debit
- 0.00 USD
- Total credit
- 5835.65 USD
No problems found. Record lengths, routing check digits, trace numbers, entry hash, totals and block count all pass.
JSON response POST /v1/ach/validate
{
"format": "nacha",
"valid": true,
"error_count": 0,
"warning_count": 0,
"summary": {
"immediate_destination": "074000010",
"immediate_destination_name": "SAMPLE BANK",
"immediate_origin": "1234567890",
"immediate_origin_name": "ACME PAYROLL INC",
"file_creation_date": "2026-10-21",
"file_id_modifier": "A",
"batches": 1,
"entries": 3,
"addenda": 2,
"returns": 0,
"notifications_of_change": 0,
"total_debit": "0.00",
"total_credit": "5835.65",
"balanced": false,
"sec_codes": [
"PPD"
],
"lines": 10
},
"issues": []
}
A PPD debit file written by an independent ACH library. Structure and totals pass; two lines without their trailing spaces and the old effective date are reported as warnings. Test file from the open-source Moov ACH project (Apache-2.0), unmodified.
ach-ppd-debit-moov.ach 891 bytes · NACHA PPD
Input file
101 23138010401210428821906240000A094101Federal Reserve Bank My Bank Name
5225Name on Account 121042882 PPDREG.SALARY 190625 1121042880000001
62723138010412345678 0100000000 Receiver Account Name 0121042880000001
82250000010023138010000100000000000000000000121042882 121042880000001
9000001000001000000010023138010000100000000000000000000
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
Validation result
VALID 0 errors · 3 warnings
- My Bank Name
- 2019-06-24
- Batches · entries
- 1 · 1
- Total debit
- 1000000.00 USD
- Total credit
- 0.00 USD
WARNING · RECORD_LENGTH line 1 The line has 75 characters instead of 94; trailing spaces seem to be missing. Many banks reject such files.
WARNING · EFFECTIVE_DATE_PAST line 2, col 70-75 The effective date 2019-06-25 is in the past; the ACH operator will settle on the next possible banking day.
WARNING · RECORD_LENGTH line 5 The line has 55 characters instead of 94; trailing spaces seem to be missing. Many banks reject such files.
JSON response POST /v1/ach/validate
{
"format": "nacha",
"valid": true,
"error_count": 0,
"warning_count": 3,
"summary": {
"immediate_destination": "231380104",
"immediate_destination_name": "Federal Reserve Bank",
"immediate_origin": "0121042882",
"immediate_origin_name": "My Bank Name",
"file_creation_date": "2019-06-24",
"file_id_modifier": "A",
"batches": 1,
"entries": 1,
"addenda": 0,
"returns": 0,
"notifications_of_change": 0,
"total_debit": "1000000.00",
"total_credit": "0.00",
"balanced": false,
"sec_codes": [
"PPD"
],
"lines": 10
},
"issues": [
{
"severity": "warning",
"rule": "RECORD_LENGTH",
"line": 1,
"found": 75,
"message": "The line has 75 characters instead of 94; trailing spaces seem to be missing. Many banks reject such files."
},
{
"severity": "warning",
"rule": "EFFECTIVE_DATE_PAST",
"line": 2,
"field": "effective_entry_date",
"column": "70-75",
"found": "190625",
"message": "The effective date 2019-06-25 is in the past; the ACH operator will settle on the next possible banking day."
},
{
"severity": "warning",
"rule": "RECORD_LENGTH",
"line": 5,
"found": 55,
"message": "The line has 55 characters instead of 94; trailing spaces seem to be missing. Many banks reject such files."
}
]
}
The same kind of file with a wrong routing check digit in the entry: the receiving bank would return it (R28). Test file from the open-source Moov ACH project (Apache-2.0), unmodified.
ach-ppd-wrong-check-digit-moov.ach 891 bytes · NACHA PPD
Input file
101 23138010401210428821906240000A094101Federal Reserve Bank My Bank Name
5225Name on Account 121042882 PPDREG.SALARY 190625 1121042880000001
62723138010512345678 0100000000 Receiver Account Name 0121042880000001
82250000010023138010000100000000000000000000121042882 121042880000001
9000001000001000000010023138010000100000000000000000000
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
Validation result
INVALID 1 error · 3 warnings
- My Bank Name
- 2019-06-24
- Batches · entries
- 1 · 1
- Total debit
- 1000000.00 USD
- Total credit
- 0.00 USD
WARNING · RECORD_LENGTH line 1 The line has 75 characters instead of 94; trailing spaces seem to be missing. Many banks reject such files.
WARNING · EFFECTIVE_DATE_PAST line 2, col 70-75 The effective date 2019-06-25 is in the past; the ACH operator will settle on the next possible banking day.
ERROR · ROUTING_CHECK_DIGIT line 3, col 12 231380105 is not a valid routing number: with these first 8 digits the check digit would be 4.
WARNING · RECORD_LENGTH line 5 The line has 55 characters instead of 94; trailing spaces seem to be missing. Many banks reject such files.
JSON response POST /v1/ach/validate
{
"format": "nacha",
"valid": false,
"error_count": 1,
"warning_count": 3,
"summary": {
"immediate_destination": "231380104",
"immediate_destination_name": "Federal Reserve Bank",
"immediate_origin": "0121042882",
"immediate_origin_name": "My Bank Name",
"file_creation_date": "2019-06-24",
"file_id_modifier": "A",
"batches": 1,
"entries": 1,
"addenda": 0,
"returns": 0,
"notifications_of_change": 0,
"total_debit": "1000000.00",
"total_credit": "0.00",
"balanced": false,
"sec_codes": [
"PPD"
],
"lines": 10
},
"issues": [
{
"severity": "warning",
"rule": "RECORD_LENGTH",
"line": 1,
"found": 75,
"message": "The line has 75 characters instead of 94; trailing spaces seem to be missing. Many banks reject such files."
},
{
"severity": "warning",
"rule": "EFFECTIVE_DATE_PAST",
"line": 2,
"field": "effective_entry_date",
"column": "70-75",
"found": "190625",
"message": "The effective date 2019-06-25 is in the past; the ACH operator will settle on the next possible banking day."
},
{
"severity": "error",
"rule": "ROUTING_CHECK_DIGIT",
"line": 3,
"field": "check_digit",
"column": "12",
"found": "5",
"expected": "4",
"message": "231380105 is not a valid routing number: with these first 8 digits the check digit would be 4."
},
{
"severity": "warning",
"rule": "RECORD_LENGTH",
"line": 5,
"found": 55,
"message": "The line has 55 characters instead of 94; trailing spaces seem to be missing. Many banks reject such files."
}
]
}
A file of returned WEB debits, as a bank sends it back. The API's returns endpoint lists each return with its code explained. Test file from the open-source Moov ACH project (Apache-2.0), unmodified.
ach-return-web-moov.ach 949 bytes · NACHA WEB
Input file
101 091400606 6910001341810170306A094101FIRST BANK & TRUST ASF APPLICATION SUPERVI
5200CoinLion 123456789 WEBTRANSFER 000101 1091000010000001
626091400606123456789 0000012354MjMxNDAwMjAtOGQPaul Jones S 1091000017611242
799R01091400600000001 09100001 091000017611242
82000000020009140060000000012354000000000000 123456789 091000010000001
5200CoinLion 123456789 WEBTRANSFER 000101 1021000020000002
621091400606867530999999 0000004565NmRjZTJmMzItMGNBob Marley S 1021000029461242
799R03091400600000003 02100002 021000029461242
82000000020009140060000000000000000000004565 123456789 021000020000002
9000002000001000000040018280120000000012354000000004565
Validation result
VALID 0 errors · 3 warnings
- ASF APPLICATION SUPERVI
- 2018-10-17
- Batches · entries
- 2 · 2
- Returns · changes
- 2 · 0
- Total debit
- 123.54 USD
- Total credit
- 45.65 USD
WARNING · EFFECTIVE_DATE_PAST line 2, col 70-75 The effective date 2000-01-01 is in the past; the ACH operator will settle on the next possible banking day.
WARNING · PURCHASE_DESCRIPTION line 2, col 54-63 Since 2026-03-20 WEB debits for online purchases of goods must use the description PURCHASE. Ignore this if these debits are not purchases (e.g. subscriptions or bills).
INFO · SERVICE_CLASS_200 line 2, col 2-4 Mixed service class 200 with debits only; some banks expect 225.
WARNING · EFFECTIVE_DATE_PAST line 6, col 70-75 The effective date 2000-01-01 is in the past; the ACH operator will settle on the next possible banking day.
INFO · SERVICE_CLASS_200 line 6, col 2-4 Mixed service class 200 with credits only; some banks expect 220.
JSON response POST /v1/ach/validate
{
"format": "nacha",
"valid": true,
"error_count": 0,
"warning_count": 3,
"summary": {
"immediate_destination": "091400606",
"immediate_destination_name": "FIRST BANK & TRUST",
"immediate_origin": "691000134",
"immediate_origin_name": "ASF APPLICATION SUPERVI",
"file_creation_date": "2018-10-17",
"file_id_modifier": "A",
"batches": 2,
"entries": 2,
"addenda": 2,
"returns": 2,
"notifications_of_change": 0,
"total_debit": "123.54",
"total_credit": "45.65",
"balanced": false,
"sec_codes": [
"WEB"
],
"lines": 10
},
"issues": [
{
"severity": "warning",
"rule": "EFFECTIVE_DATE_PAST",
"line": 2,
"field": "effective_entry_date",
"column": "70-75",
"found": "000101",
"message": "The effective date 2000-01-01 is in the past; the ACH operator will settle on the next possible banking day."
},
{
"severity": "warning",
"rule": "PURCHASE_DESCRIPTION",
"line": 2,
"field": "company_entry_description",
"column": "54-63",
"found": "TRANSFER",
"expected": "PURCHASE",
"message": "Since 2026-03-20 WEB debits for online purchases of goods must use the description PURCHASE. Ignore this if these debits are not purchases (e.g. subscriptions or bills)."
},
{
"severity": "info",
"rule": "SERVICE_CLASS_200",
"line": 2,
"field": "service_class_code",
"column": "2-4",
"found": "200",
"message": "Mixed service class 200 with debits only; some banks expect 225."
},
{
"severity": "warning",
"rule": "EFFECTIVE_DATE_PAST",
"line": 6,
"field": "effective_entry_date",
"column": "70-75",
"found": "000101",
"message": "The effective date 2000-01-01 is in the past; the ACH operator will settle on the next possible banking day."
},
{
"severity": "info",
"rule": "SERVICE_CLASS_200",
"line": 6,
"field": "service_class_code",
"column": "2-4",
"found": "200",
"message": "Mixed service class 200 with credits only; some banks expect 220."
}
]
}
JSON response POST /v1/ach/returns
{
"returns": 2,
"notifications_of_change": 0,
"other_entries": 0,
"returned_amount": "169.19",
"by_code": {
"R01": 1,
"R03": 1
},
"items": [
{
"kind": "return",
"code": "R01",
"title": "Insufficient funds",
"explanation": "The account did not have enough money for the debit.",
"action": "You may try again up to two more times within 180 days of the original effective date.",
"retry_allowed": true,
"original_trace_number": "091400600000001",
"original_rdfi": "09100001",
"date_of_death": null,
"addenda_information": "",
"addenda_line": 4,
"entry": {
"batch": 1,
"line": 3,
"sec_code": "WEB",
"company_name": "CoinLion",
"company_identification": "123456789",
"description": "TRANSFER",
"effective_date": "2000-01-01",
"transaction_code": "26",
"type": "debit",
"account_type": "checking",
"prenote": false,
"routing": "091400606",
"account": "123456789",
"amount": "123.54",
"name": "Paul Jones",
"identification_number": "MjMxNDAwMjAtOGQ",
"trace_number": "091000017611242"
}
},
{
"kind": "return",
"code": "R03",
"title": "No account / unable to locate account",
"explanation": "The account number does not exist at this bank, or does not match the name.",
"action": "Check the account number and routing number with the receiver.",
"retry_allowed": false,
"original_trace_number": "091400600000003",
"original_rdfi": "02100002",
"date_of_death": null,
"addenda_information": "",
"addenda_line": 8,
"entry": {
"batch": 2,
"line": 7,
"sec_code": "WEB",
"company_name": "CoinLion",
"company_identification": "123456789",
"description": "TRANSFER",
"effective_date": "2000-01-01",
"transaction_code": "21",
"type": "credit",
"account_type": "checking",
"prenote": false,
"routing": "091400606",
"account": "867530999999",
"amount": "45.65",
"name": "Bob Marley",
"identification_number": "NmRjZTJmMzItMGN",
"trace_number": "021000029461242"
}
}
],
"note": "Explanations are our own summaries, not the text of the Nacha Operating Rules."
}
How it works
- Make a CSV with a header row:
name,routing,account,amount, and optionallytype(credit or debit),account_type,identification_number,addenda,prenote. - Send it as
entries_csvto the API’sPOST /v1/ach/generate, with your company details, the SEC code and the effective date. - You get the NACHA file, or with
return_report=trueJSON with the file and its validation report. If anything is wrong, you get the problems instead of a file.
The CSV and the request
The CSV behind the first sample (made-up names, accounts and routing numbers):
name,routing,account,amount,account_type,addenda
Maria Lopez,121000358,4400123456,1850.00,checking,October salary
Sam O'Brien,022000062,77-1290,2310.45,savings,October salary
Lee Chen,061000159,9100022,1675.20,checking,
The request (live on RapidAPI):
curl -X POST "https://<host>/v1/ach/generate" -H "Content-Type: application/json" -d '{
"immediate_destination": "074000010", "immediate_destination_name": "Sample Bank",
"immediate_origin": "1234567890", "immediate_origin_name": "Acme Payroll Inc",
"company_name": "Acme Payroll", "company_id": "1234567890",
"sec_code": "PPD", "description": "PAYROLL", "effective_date": "2026-10-23",
"entries_csv": "name,routing,account,amount,account_type,addenda\nMaria Lopez,121000358,..."
}' -o payroll.ach
The file is returned only after it has passed the same validator as the NACHA file validator.
Entries can also be sent as a JSON list (entries) instead of a CSV. The way back works too: POST /v1/ach/parse?format=csv
lists the entries of a NACHA file as CSV.
Sample files marked “Moov” are unmodified test files from the open-source Moov ACH project (Apache License 2.0). Not affiliated with Nacha.
Questions
Which SEC codes can be generated?
PPD (consumer payroll and debits), CCD (business to business), WEB (internet-authorized debits) and TEL (telephone-authorized debits). One batch per file. CTX and IAT are not generated.
What does the generator do for me?
It checks every routing check digit and computes the trace numbers from your bank’s routing number, the entry hash, the debit and credit totals and the block count, picks the service class (220, 225, or 200 with an offset) and pads the file to blocks of 10.
What happens to names with accents or long text?
Text is converted to upper-case ASCII (ü → UE) and cut to the field width. Both are reported as warnings, so nothing changes silently. Amounts are never rounded.
My bank wants a balanced file. Is that possible?
Yes. Send your own account as offset; the generator adds the offset entry so debits and credits match.
Is this tool from Nacha?
No. It is not affiliated with Nacha.
Need it in your own software?
The same engine is available as an API: whole files instead of a preview, every export format, automation.
Related tools
pain.001 validator
Check a pain.001 or pain.008 file against the ISO 20022 schema and the EPC rules, with every problem explained.
pain.001.001.03 to .09
Migrate 2009 SEPA files to the 2019 versions, checked against the new schema and the SEPA rules.
NACHA file validator
Check a NACHA ACH file record by record, with each problem's line, column and a plain explanation.