# Validate Bank Accounts

import { Tabs, TabItem } from '@astrojs/starlight/components';

## Overview

Before initiating a transaction or linking a manually entered bank account, it's important to ensure the account and
routing number combination is valid. GrailPay offers a dedicated endpoint that validates these details in real-time to
reduce the risk of failed transactions and ACH returns.

This validation is especially useful during onboarding or when updating a user's financial information, as it helps
ensure the account is active and properly formatted for ACH.

:::note
This endpoint only supports manually entered bank accounts. For accounts linked through Bank Link or Plaid,
validation is handled as part of the linking process.
:::

---

## Use Cases

You might use this endpoint when:

- Collecting account details via a custom onboarding form
- Adding a bank account outside the registration flow
- Verifying bank details before submitting a transaction
- Implementing safeguards to reduce return-related fraud
- Performing name matching verification against account holder information

---

## Account Intelligence Versions

The response payload varies depending on which version of account intelligence you select. We recommend using **V3** for the most comprehensive validation insights.

These are versions of the Account Intelligence service, chosen per request with `actions.account_intelligence.version`. They are unrelated to the Payments API version, which is v3 for every endpoint on this site.

:::caution
**V1 Deprecation Notice:** Version 1 account intelligence is deprecated and will be removed in a future release.
Please migrate to V3 for enhanced decisioning insights.
:::

### Version 3 (Recommended)

V3 provides comprehensive decisioning insights including transaction history, name matching, and duplicate detection.

For complete request and response schema, refer to:
- [Validate Account & Routing V3](/api/payments/#tag/bank-accounts/POST/api/v3/bank-accounts/validate)

:::note
To enable name matching, set `name_match: true` in the `actions.account_intelligence` object of your request payload.
When name matching is enabled, the `person` object with `first_name` and `last_name` is required.
If you are not using name matching, the `person` object is optional.
:::

#### V3 Response Fields

To learn more about our V3 Response fields, refer to the [Account Intelligence V3 Documentation](/docs/risk/bank-accounts/account-intelligence).

---

### Version 2

V2 returns a confidence score with a human-readable confidence level.
```json
{
  "status": true,
  "message": "Validation processed successfully",
  "data": {
    "account_intelligence": {
      "confidence_score": 0.81,
      "confidence_level": "very high",
      "version": "v2"
    }
  },
  "errors": null,
  "request_id": "2d6a4c39-8fcf-4d80-9189-b4764eac31f2"
}
```

#### V2 Response Fields

| Field              | Type    | Description                                                                  |
|--------------------|---------|------------------------------------------------------------------------------|
| `confidence_score` | decimal | Overall confidence score (0.0 - 1.0)                                         |
| `confidence_level` | string  | Human-readable confidence level (e.g., `very high`, `high`, `medium`, `low`) |

---

### Version 1 (Deprecated)

:::caution
V1 is deprecated. Please migrate to V3.
:::

V1 returns a simple validity status.
```json
{
  "status": true,
  "message": "Validation processed successfully",
  "data": {
    "account_intelligence": {
      "validity": "valid",
      "version": "v1"
    }
  },
  "errors": null,
  "request_id": "2d6a4c39-8fcf-4d80-9189-b4764eac31f2"
}
```

#### V1 Response Fields

| Field      | Type   | Description                                               |
|------------|--------|-----------------------------------------------------------|
| `validity` | string | Validation result: `valid`, `invalid`, or `not_validated` |

---

## Testing

You can test the validation endpoint in our [sandbox environment](/docs/technical/environments#sandbox). The sandbox reserves a set
of routing numbers that each return a fixed validation outcome, letting you build against every result.

When using **V3** as the Account Intelligence version, the sandbox test routing numbers and the exact values each one
returns are documented in full on the [Account Intelligence](/docs/risk/bank-accounts/account-intelligence#sandbox-testing)
page. Refer there for the complete V3 testing reference.

A quick summary of the primary routing numbers:

<Tabs>
  <TabItem label="Low Risk ( valid account with name match )">
    ```json
    {
        "account_number": "1234567890",
        "routing_number": "000000001"
    }
    ```
  </TabItem>
  <TabItem label="Medium Risk ( name match not available )">
    ```json
    {
        "account_number": "1234567890",
        "routing_number": "000000002"
    }
    ```
  </TabItem>
  <TabItem label="High Risk ( negative history )">
    ```json
    {
        "account_number": "1234567890",
        "routing_number": "000000003"
    }
    ```
  </TabItem>
  <TabItem label="Simulates 503 error (service unavailable)">
    ```json
    {
        "account_number": "1234567890",
        "routing_number": "999999999"
    }
    ```
  </TabItem>
</Tabs>

### Version 2 Testing

When using `v2` as the Account Intelligence version in the `actions` object of your request payload, the same routing
numbers can be used to simulate different validation outcomes. Version 2 responses include a confidence score and
confidence level, but do not include the detailed insights provided in Version 3.

- `000000001` — High confidence score (e.g., 0.81) with a confidence level of `very_high`
- `000000002` — Medium confidence score (e.g., 0.65) with a confidence level of `medium`
- `000000003` — Low confidence score (e.g., 0.30) with a confidence level of `low`

### Version 1 Testing

When using `v1` as the Account Intelligence version in the `actions` object of your request payload, the same routing
numbers can be used to simulate different validation outcomes. Version 1 responses only indicate whether the account
is `valid`, `invalid`, or `not_validated` — no confidence score or level is provided.

- `000000001` — Returns `valid`
- `000000002` — Returns `invalid`
- `000000003` — Returns `not_validated`

---

## Next Steps

After successful validation, you can proceed to [Add the Bank Account](/docs/technical/bank-accounts/add-bank-account)
to the user profile, or use the validated details to initiate a transaction.

For complete request and response schema, refer to:
- [Validate Account & Routing V3](/api/payments/#tag/bank-accounts/POST/api/v3/bank-accounts/validate)

---