Braintree ACH Integration

Process ACH (bank account) transactions through Preczn with Braintree as your processor


📘

Prerequisites

Ensure you've completed Braintree Connection Setup before proceeding.

See also: For general ACH concepts and transaction types, see ACH (Direct Debit).

Setup

Before processing ACH transactions, you must configure Braintree webhooks. This is a one-time setup per merchant.

❗️

Critical

You must complete webhook configuration before processing ACH transactions. Without it, ACH sales will remain in PENDING status indefinitely and you won't receive settlement confirmations.

Get Your Webhook URL

Option A: Via Dashboard

  1. Navigate to Merchant Connection settings in the Preczn dashboard
  2. Locate the Braintree processor connection
  3. Copy the Webhook Consumer API URL

Option B: Via API

GET /v1/merchants/{merchantId}
{
  "id": "mid_2zyd88xrnr90xskmjmqpd0x1vj",
  "connections": [
    {
      "processor": "braintree",
      "ingressWebhook": {
        "url": "https://wc-api.preczn.com/v1/transactions/braintree/platform/{platformId}/merchant/{merchantId}",
        "verified": false
      }
    }
  ]
}

Configure Braintree

In your Braintree Control Panel:

  1. Click Settings gearAPIWebhooks tab
  2. Create a new webhook destination using the URL from above
  3. Subscribe to: Transaction Settled, Transaction Settlement Declined, and optionally Transaction Disbursed

Verify the Connection

  1. Click "Check URL" in Braintree webhook settings
  2. Braintree sends a test webhook to Preczn
  3. Verify success: ingressWebhook.verified changes to true
{
  "connections": [
    {
      "processor": "braintree",
      "ingressWebhook": {
        "url": "https://wc-api.preczn.com/v1/transactions/braintree/platform/{platformId}/merchant/{merchantId}",
        "verified": true
      }
    }
  ]
}
👍

Tip

If verification fails, double-check that your Braintree credentials (Merchant ID, Public Key, Secret Key) are correctly configured in Preczn.

How ACH Works with Braintree

ACH transactions follow a verify-then-charge pattern:

  1. Verify - Tokenize and verify the bank account (required once per token)
  2. Sale - Charge the verified bank account
  3. Refund - Return funds after settlement (if needed)

Preczn uses Braintree's Network Check verification method, which instantly validates the routing number, account number, and customer information against banking networks.

🚧

Important

  • Verification is instant - no micro-deposit waiting period
  • Network Check is not supported by all banks - verification may fail with valid accounts in rare cases
  • ACH does not support authorization, capture, or void - use sale and refund only

Integration Guide

Required Fields

When verifying ACH bank accounts, you must provide these fields:

🚧

Important

These fields are validated by Braintree's API. Missing fields cause Braintree to reject the verification.

Always Required

FieldDescription
achMandateCustomer authorization text (e.g., "I authorize Acme Corp to debit my account...")
billingAddress.addressStreet address
billingAddress.cityCity
billingAddress.regionState/region (e.g., "TX")
billingAddress.postalPostal/ZIP code

Conditional by Account Type

FieldRequired WhenDescription
firstName + lastNamePersonal accountsFor personalChecking or personalSavings
accountBusinessNameCorporate accountsFor corporateChecking or corporateSavings

ACH Mandate

The achMandate contains authorization text the customer agreed to. Example:

"I authorize [Your Company Name] to debit my bank account for the agreed upon amount."
⚠️

Compliance Notice

Preczn passes the achMandate to Braintree but is not responsible for ACH authorization compliance. See Braintree ACH Client-side Implementation for requirements.

Verify and Charge Flow

This section covers the standard integration pattern for ACH transactions.

👍

Recommended

Always submit a verify request before charging—Preczn handles both verified and unverified tokens transparently. This eliminates tracking verification status in your system.

Step 1: Verify the Bank Account

Submit a verify request with the token and required fields.

Personal Account:

POST /v1/transactions?tokenize=true
{
  "type": "verify",
  "merchantId": "mid_2zyd88xrnr90xskmjmqpd0x1vj",
  "payment": {
    "token": "tkn_SINGLE_USE_TOKEN_FROM_SDK"
  },
  "firstName": "Jane",
  "lastName": "Smith",
  "email": "[email protected]",
  "achMandate": "I authorize Acme Corp to debit my account for the agreed upon amount.",
  "billingAddress": {
    "address": "456 Oak Street",
    "city": "Austin",
    "region": "TX",
    "postal": "78702",
    "country": "USA"
  }
}

Corporate Account:

POST /v1/transactions?tokenize=true
{
  "type": "verify",
  "merchantId": "mid_2zyd88xrnr90xskmjmqpd0x1vj",
  "payment": {
    "token": "tkn_SINGLE_USE_TOKEN_FROM_SDK"
  },
  "accountBusinessName": "Johnson Industries LLC",
  "email": "[email protected]",
  "achMandate": "I authorize Acme Corp to debit the Johnson Industries LLC account for the agreed upon amount.",
  "billingAddress": {
    "address": "123 Business Park Dr",
    "address2": "Suite 400",
    "city": "Austin",
    "region": "TX",
    "postal": "78701",
    "country": "USA"
  }
}

Response:

{
  "id": "txn_01h4x5y6z7a8b9c0d1e2f3g4h5",
  "type": "verify",
  "authorization": {
    "status": "A"
  },
  "payment": {
    "token": "tkn_01j2k3l4m5n6o7p8q9r0s1t2u3",
    "type": "personalChecking",
    "last4": "3210",
    "bin": "011000015"
  },
  "firstName": "Jane",
  "lastName": "Smith",
  "achMandate": "I authorize Acme Corp to debit my account for the agreed upon amount."
}
📘

Key Points

  • Use ?tokenize=true to receive the multi-use token in the response
  • Verification is instant (status: "A" or error) - no pending state
  • Store the returned payment.token for future charges

Step 2: Charge the Verified Token

POST /v1/transactions
{
  "type": "sale",
  "merchantId": "mid_2zyd88xrnr90xskmjmqpd0x1vj",
  "amount": 5000,
  "currency": "USD",
  "payment": {
    "token": "tkn_01j2k3l4m5n6o7p8q9r0s1t2u3"
  }
}

Response:

{
  "id": "txn_01y2z3a4b5c6d7e8f9g0h1i2j3",
  "type": "sale",
  "authorization": {
    "status": "P",
    "processorTransactionId": "abc123xyz"
  },
  "amount": 5000,
  "currency": "USD",
  "payment": {
    "token": "tkn_01j2k3l4m5n6o7p8q9r0s1t2u3",
    "type": "personalChecking",
    "last4": "3210",
    "bin": "011000015"
  },
  "achMandate": "I authorize Acme Corp to debit my account for the agreed upon amount."
}
📘

Pending Status

ACH sales return status: "P" (PENDING) initially. Final status arrives via webhook after 1-3 business days.

Charging Existing Tokens

For tokens already on file, submit a verify request before charging. Preczn handles both scenarios:

  • Already verified: Returns success immediately (no-op)
  • Not yet verified: Verifies at Braintree, then returns success

This approach eliminates tracking verification status—always verify, then charge.

Direct Charge (Advanced)

If you track verification status externally, you can skip verify for known-verified tokens:

{
  "type": "sale",
  "merchantId": "mid_2zyd88xrnr90xskmjmqpd0x1vj",
  "amount": 10000,
  "currency": "USD",
  "payment": {
    "token": "tkn_VERIFIED_MULTI_USE_TOKEN"
  }
}
🚧

Important

If the token isn't verified, this fails with 400 Bad Request. Use the verify-then-charge pattern for simpler integrations.

📘

Server-Side Tokenization

Need to create tokens from existing bank account data? See Server-Side Bank Account Tokenization for the POST /v1/tokens endpoint and direct tokenization methods.


Troubleshooting

Error Reference

Validation Errors (400)

MessageCauseSolution
ACH mandate is required for Braintree ACH transactionsMissing achMandateAdd achMandate to request
Personal ACH accounts require firstName and lastNamePersonal account missing nameAdd firstName and lastName
Corporate ACH accounts require accountBusinessName or firstName and lastNameCorporate account missing nameAdd accountBusinessName
Routing number must be exactly 9 digitsInvalid routing numberVerify routing number format
Bank country must be one of: USANon-USA bankACH is US-only

Sale Without Verification (400)

{
  "message": "ACH bank account not verified. Submit a verify transaction first.",
  "error": "Bad Request",
  "statusCode": 400
}
👍

Solution

Submit a verify transaction with required fields before the sale:

  1. POST /v1/transactions with type: "verify"
  2. Wait for status: "A"
  3. POST /v1/transactions with type: "sale"

Status Codes

StatusMeaningAction
AApprovedSuccess
PPendingWait for webhook (normal for ACH sales)
DDeclinedContact customer for alternative payment
EErrorCheck error message, retry with corrections

Refunds

ACH refunds can only be processed after settlement (typically 3 business days). See ACH Refunds for details.

For a complete error list, see ACH Error Reference.

Webhook Events

ACH transactions settle asynchronously. Configure webhooks to receive final status.

📘

Reminder

If webhooks aren't configured, see Setup above.

Event Mapping

Braintree EventPreczn EventDescription
transaction_settledtransaction.approvedSettlement succeeded
transaction_settlement_declinedtransaction.declinedBank declined
Sale submittedtransaction.pendingAwaiting settlement
Processing errortransaction.erroredProcessing failed
📘

Timeline

Settlement typically occurs 1-3 business days after the sale.

Payload Examples

Approved:

{
  "id": "evt_01h4x5y6z7a8b9c0d1e2f3g4h5",
  "webhookId": "wbh_01a2b3c4d5e6f7g8h9i0j1k2l3",
  "eventType": "transaction.approved",
  "data": {
    "id": "txn_01h4x5y6z7a8b9c0d1e2f3g4h5",
    "type": "sale",
    "authorization": {
      "status": "A",
      "processorTransactionId": "abc123xyz"
    },
    "amount": 5000,
    "currency": "USD",
    "payment": {
      "type": "personalChecking",
      "last4": "3210",
      "bin": "011000015"
    }
  }
}

Declined:

{
  "id": "evt_01h4x5y6z7a8b9c0d1e2f3g4h5",
  "webhookId": "wbh_01a2b3c4d5e6f7g8h9i0j1k2l3",
  "eventType": "transaction.declined",
  "data": {
    "id": "txn_01h4x5y6z7a8b9c0d1e2f3g4h5",
    "type": "sale",
    "authorization": {
      "status": "D",
      "processorTransactionId": "abc123xyz"
    },
    "amount": 5000,
    "currency": "USD"
  }
}

Testing

Braintree provides test values for ACH in test mode:

  • Test routing numbers - Valid routing numbers for test mode
  • Test account numbers - Trigger different verification outcomes
  • Test amounts - Trigger different settlement outcomes via webhooks

See Braintree ACH Testing for current test values.

Best Practices

  1. Always verify before charging - Use the verify-then-charge pattern
  2. Implement webhook handlers - Essential for final transaction status
  3. Handle PENDING status - ACH sales don't complete immediately
  4. Store multi-use tokens - Reuse verified tokens for recurring charges
  5. Collect complete billing address - Required for Braintree verification