Skip to content

ACH Validation API

Overview

The FISTWorks ACH Validation API lets you validate ACH files programmatically from your own applications, scripts, or automated workflows. Submit an ACH file and receive detailed NACHA compliance results — the same validation engine that powers the web-based ACH Validator.

The API is available on Plus and Max plans.

Getting Started

Request API Credentials

  1. Sign in to FISTWorks and navigate to your Organization page.
  2. Contact us through the Contact page to request API access for your organization.
  3. We will provision OAuth2 client credentials (Client ID and Client Secret) for your application.

Authentication

The API uses OAuth2 client credentials for authentication. Your application requests an access token using your Client ID and Client Secret, then includes that token in API requests.

Request an access token:

POST https://login.fistworks.com/{tenant-id}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

client_id={YOUR_CLIENT_ID}
&client_secret={YOUR_CLIENT_SECRET}
&scope={API_SCOPE}/.default
&grant_type=client_credentials

The token endpoint and scope values are provided when your API credentials are provisioned.

Use the token in API requests:

Include the access token in the Authorization header:

Authorization: Bearer {ACCESS_TOKEN}

Access tokens expire after one hour. Request a new token when the current one expires.

Endpoints

Validate ACH File (JSON)

Submit ACH file content as a JSON string for validation.

POST /app/api/v1/ach/validate
Content-Type: application/json
Authorization: Bearer {ACCESS_TOKEN}

Request body:

{
  "fileContent": "101 091000019 1234567891...",
  "options": {
    "strictMode": false,
    "validateRoutingNumbers": true
  }
}

Validate ACH File (Upload)

Upload an ACH file directly as multipart form data.

POST /app/api/v1/ach/validate/upload
Content-Type: multipart/form-data
Authorization: Bearer {ACCESS_TOKEN}

Form fields:

Field Type Required Description
file File Yes The ACH file to validate (.txt or .ach, max 1 MB)
strictMode Boolean No Convert warnings to errors (default: false)

Validation Options

All options are optional. Defaults provide standard NACHA validation.

Option Type Default Description
strictMode bool false Convert warnings to errors for stricter compliance
validateRoutingNumbers bool true Verify ABA routing number check digits
validateDateRanges bool true Check that file dates are reasonable
validateSECCodeRules bool true Validate SEC code specific requirements
validateControlTotals bool true Verify counts, hashes, and amounts
validateRequiredFields bool true Ensure required fields are not empty
validateHeaderControlConsistency bool true Header and control record consistency
validateSameDayACHLimits bool true Check same-day ACH transaction limits
sameDayACHLimit decimal 1000000 Same-day ACH limit threshold
largeAmountThreshold decimal 100000 Threshold for large amount warnings
validateAddendaRecords bool false Validate addenda record content
validateBalancedFile bool false Verify total debits equal total credits
detectDuplicates bool true Detect duplicate transactions by trace number, amount, and routing

Response Format

A successful validation request returns a JSON response with the validation results:

{
  "isValid": true,
  "isPerfect": false,
  "errorCount": 0,
  "warningCount": 2,
  "infoCount": 1,
  "summary": "File is valid with 2 warnings",
  "statistics": {
    "batchCount": 1,
    "entryCount": 5,
    "totalDebits": 1500.00,
    "totalCredits": 1500.00,
    "immediateDestination": "091000019",
    "immediateOrigin": "123456789",
    "fileCreationDate": "250126"
  },
  "messages": [
    {
      "severity": "Warning",
      "category": "BusinessLogic",
      "code": "LARGE-AMOUNT",
      "message": "Transaction amount exceeds threshold",
      "location": "Batch 0, Entry 2",
      "fieldName": "Amount",
      "actualValue": "150000",
      "expectedValue": "< 100000"
    }
  ]
}

Response Fields

Field Type Description
isValid bool true if the file has no errors (warnings are allowed)
isPerfect bool true if the file has no errors, warnings, or info messages
errorCount int Number of error-level messages
warningCount int Number of warning-level messages
infoCount int Number of informational messages
summary string Human-readable summary of the validation result
statistics object File-level statistics (batch count, entry count, totals)
messages array Detailed validation messages with severity, location, and context

Message Severity Levels

Severity Meaning
Error NACHA rule violation — the file will be rejected by the ACH network
Warning Potential issue that may cause problems but is not a strict rule violation
Info Informational observation about the file structure or contents

HTTP Status Codes

Code Description
200 Validation completed — check isValid for the result
400 Invalid request — missing file content or unparseable file
401 Missing or invalid access token
403 Access token does not include the required API role
413 File exceeds the 1 MB size limit
429 Rate limit exceeded — wait and retry
500 Internal server error

Rate Limits

Plan Daily API Requests
Plus 300 requests per day
Max Unlimited

When you exceed your daily limit, the API returns HTTP 429. Limits reset at midnight UTC.

Code Examples

cURL

# Get access token
TOKEN=$(curl -s -X POST "{TOKEN_ENDPOINT}" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id={CLIENT_ID}" \
  -d "client_secret={CLIENT_SECRET}" \
  -d "scope={API_SCOPE}/.default" \
  -d "grant_type=client_credentials" \
  | jq -r '.access_token')

# Validate a file
curl -X POST "https://app.fistworks.com/app/api/v1/ach/validate/upload" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@/path/to/ach-file.txt" \
  -F "strictMode=true"

Python

import requests

# Get access token
token_response = requests.post(
    "{TOKEN_ENDPOINT}",
    data={
        "client_id": "{CLIENT_ID}",
        "client_secret": "{CLIENT_SECRET}",
        "scope": "{API_SCOPE}/.default",
        "grant_type": "client_credentials"
    }
)
access_token = token_response.json()["access_token"]

# Validate a file
with open("ach-file.txt", "r") as f:
    file_content = f.read()

response = requests.post(
    "https://app.fistworks.com/app/api/v1/ach/validate",
    headers={"Authorization": f"Bearer {access_token}"},
    json={
        "fileContent": file_content,
        "options": {"strictMode": True}
    }
)

result = response.json()
print(f"Valid: {result['isValid']}, Errors: {result['errorCount']}")

C# (.NET)

using System.Net.Http.Headers;

// Get access token
var tokenClient = new HttpClient();
var tokenRequest = new FormUrlEncodedContent(new Dictionary<string, string>
{
    ["client_id"] = "{CLIENT_ID}",
    ["client_secret"] = "{CLIENT_SECRET}",
    ["scope"] = "{API_SCOPE}/.default",
    ["grant_type"] = "client_credentials"
});

var tokenResponse = await tokenClient.PostAsync("{TOKEN_ENDPOINT}", tokenRequest);
var tokenJson = await tokenResponse.Content.ReadFromJsonAsync<JsonElement>();
var accessToken = tokenJson.GetProperty("access_token").GetString();

// Validate a file
var apiClient = new HttpClient();
apiClient.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", accessToken);

var response = await apiClient.PostAsJsonAsync(
    "https://app.fistworks.com/app/api/v1/ach/validate",
    new
    {
        fileContent = File.ReadAllText("ach-file.txt"),
        options = new { strictMode = true }
    });

var result = await response.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine($"Valid: {result.GetProperty("isValid")}");

JavaScript / Node.js

// Get access token
const tokenResponse = await fetch('{TOKEN_ENDPOINT}', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    client_id: '{CLIENT_ID}',
    client_secret: '{CLIENT_SECRET}',
    scope: '{API_SCOPE}/.default',
    grant_type: 'client_credentials'
  })
});
const { access_token } = await tokenResponse.json();

// Validate a file
const response = await fetch(
  'https://app.fistworks.com/app/api/v1/ach/validate',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${access_token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      fileContent: fileContent,
      options: { strictMode: true }
    })
  }
);

const result = await response.json();
console.log('Valid:', result.isValid);

Tips

  • Use the upload endpoint for simplicity when validating files from disk. Use the JSON endpoint when your application already has the file content in memory.
  • Enable strictMode for production pre-submission checks to catch issues that may not be errors but could cause bank rejections.
  • Enable detectDuplicates to catch accidentally repeated entries before they reach the bank.
  • Enable validateBalancedFile when your ACH files are expected to have matching debits and credits (e.g., payroll with offsetting entries).
  • Cache your access token for its full lifetime (one hour) to avoid unnecessary token requests.
  • Contact us through the Contact page for API access provisioning or technical support.