> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grailpay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Account Intelligence

## Overview

The Account Intelligence v3 endpoint is part of GrailPay's real-time bank account validation system. It allows you to
determine whether a user's bank account is valid, active, and safe to use — helping you reduce fraud, prevent returns,
and accelerate decision-making.

With a single API call, the endpoint returns a `confidence_score` along with detailed `decisioning_insights` based on
the identity and bank account data you provide. These insights can be used to drive onboarding, funding, and payout
workflows with higher accuracy and lower risk.

When constructing your request, you'll pass an `identity` object that contains either an `individual` or an
`organization`. These two are mutually exclusive — the request will fail if both are included. Use `organization` for
validating business entities and `individual` for people.

While the only required fields are account and routing numbers, we strongly recommend providing as much identity data
as possible. More complete input data results in more meaningful confidence scores and more actionable insights.

<Warning>
  This API is only available for U.S. bank accounts.
</Warning>

For complete request and response definitions, visit the [Account Intelligence OpenAPI Documentation](https://risk-api.grailpay.com/spec/#/Account%20Validation/post_api_v3_accounts_validate).

***

## Sandbox Testing

The sandbox reserves a small set of routing numbers that return a fixed result. Use them to build and test against each
outcome so you can see how real results will behave. These reserved routing numbers apply across the Risk API, including
both the Account Intelligence and Transaction Intelligence models.

Sandbox base URL: `https://risk-api-sandbox.grailpay.com`

### There Are No Test Account Numbers

Selection is driven by the `routing_number` alone. The account number can be any value that passes standard validation —
numeric, 17 digits or fewer, and not all zeros.

For example, `000000001` paired with any valid account number returns the full-match result. Pairing that same account
number with `000000003` returns the high-risk result.

### Test Routing Numbers

| Routing Number  | Scenario                                  | HTTP Status |
| --------------- | ----------------------------------------- | ----------- |
| `000000001`     | Low risk, full match                      | `200`       |
| `000000002`     | Medium risk, match not available          | `200`       |
| `000000003`     | High risk, negative history               | `200`       |
| `000000004`     | Close taxpayer match                      | `200`       |
| empty / missing | Validation-style errors                   | `400`       |
| `999999999`     | Upstream unavailable                      | `503`       |
| Any other value | Falls through to live sandbox decisioning | live        |

### What Each Routing Number Returns

For calls to `POST /api/v3/accounts/validate`, each reserved routing number returns the following values:

| Routing Number | `confidence_score` | `name_match`    | `name_match_score` | `taxpayer_number_match` |
| -------------- | ------------------ | --------------- | ------------------ | ----------------------- |
| `000000001`    | `0.91`             | `yes`           | `0.98`             | `yes`                   |
| `000000002`    | `0.53`             | `not_available` | `0.0`              | `not_available`         |
| `000000003`    | `0.12`             | `no`            | `0.14`             | `no`                    |
| `000000004`    | `0.65`             | `not_available` | `0.0`              | `close_match`           |

<Note>
  `confidence_score` is returned at the top level of the response. `name_match`, `name_match_score`, and
  `taxpayer_number_match` are returned under `decisioning_insights`.
</Note>

***

## Actions

By default, every call to this endpoint performs core validation checks on the routing and account numbers. To enrich
the results further, you can optionally include an `actions` object in your request to enable additional validation
layers.

Currently, the following actions are supported:

* `name_match`: Compares the provided name in the identity object with the account holder's name on file.
* `taxpayer_number_match`: Compares the provided taxpayer identifier in the identity object with the account holder's tax ID on file.

To enable either action, include it in the `actions` object of your request payload:

```json theme={"system"}
{
  "actions": {
    "name_match": true,
    "taxpayer_number_match": true
  }
}
```

Each enabled action runs an additional identity check that contributes directly to the confidence score and surfaces a
corresponding attribute in the `decisioning_insights` object. Enabling `name_match` returns `name_match` and
`name_match_score`; enabling `taxpayer_number_match` returns `taxpayer_number_match`.

For full request and response examples, see the [Account Intelligence API reference](https://risk-api.grailpay.com/spec/#/Account%20Validation/post_api_v3_accounts_validate).

***

## Response Attributes

Every call to the Account Intelligence endpoint returns a `confidence_score` alongside a set of structured
`decisioning_insights`. Together, these give you both a single risk signal and the explainable attributes behind it,
so you can plug them directly into your own models or rules-based decisioning.

### Confidence Score

The `confidence_score` is a number between `0.00` and `1.00` representing the likelihood that an ACH debit or credit
will be processed successfully without an operational return. Higher is better — a score of `0.95` indicates high
confidence that the account is open, active, and able to transact, while a score of `0.30` indicates significant risk
of an ACH return.

The score is produced by a proprietary machine learning model trained on millions of historical ACH transactions and
their outcomes. The model takes the account and routing number, enriches it with third-party data sources and GrailPay's
own first-party intelligence, and returns a calibrated probability.

As a general guide, the score maps to the following confidence levels:

| Confidence Level | Score Range     |
| ---------------- | --------------- |
| Very High        | `0.90` – `1.00` |
| Good             | `0.70` – `0.89` |
| OK               | `0.40` – `0.69` |
| Risky            | `0.00` – `0.39` |

### Decisioning Insights

The `decisioning_insights` object contains explainable attributes returned alongside the score. Each attribute is
designed to be consumed directly by your risk engine — as an input to your own models or as a building block for
rules-based logic.

| Attribute                     | What It Means                                                                       | Why It Matters                                                                                                                      | Typical Interpretation                                                                                                                                                              | Possible Values                                                                                                                             |
| ----------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `valid_routing_number`        | Confirms the routing number belongs to a legitimate, active financial institution.  | An invalid routing number will generally cause the payment to fail.                                                                 | `true` = valid routing number. `false` = do not process without review.                                                                                                             | Boolean, `true` / `false`                                                                                                                   |
| `name_match`                  | Compares the provided account owner's name to the name associated with the account. | Helps confirm the person or business owns the account.                                                                              | `yes` = positive. `no` = name does not appear to match the account owner; review for possible incorrect information or identity concerns. `not_available` = no comparison possible. | String: `yes`, `no`, `not_available`. Returned only when `name_match` is enabled in the request `actions` object.                           |
| `name_match_score`            | Measures how closely the names match on a scale from `0.00` to `1.00`.              | Provides additional detail beyond a simple yes/no result.                                                                           | Near `1.00` = strong match. Around `0.80` = minor differences. Lower scores = weak match.                                                                                           | Number, `0.00` to `1.00`. Returns `0.00` when no name was supplied, or when no identity data source could evaluate the name.                |
| `days_since_first_seen`       | How long the account has existed in GrailPay's data network.                        | Older accounts generally provide more confidence.                                                                                   | Higher values = more established. `-1` = no history available.                                                                                                                      | Integer. `-1` if unknown.                                                                                                                   |
| `days_since_last_transaction` | Number of days since the account was last active.                                   | Active accounts tend to be lower risk than dormant accounts.                                                                        | Lower values = recently active. Higher values = less recent activity.                                                                                                               | Integer. `-1` if unknown.                                                                                                                   |
| `positive_transactions_seen`  | Indicates successful historical transactions.                                       | Past successful payments increase confidence.                                                                                       | `true` = positive history.                                                                                                                                                          | Boolean, `true` / `false`                                                                                                                   |
| `negative_transactions_seen`  | Indicates returned payments or other negative events.                               | Past issues may increase risk.                                                                                                      | `true` = review more closely.                                                                                                                                                       | Boolean, `true` / `false`                                                                                                                   |
| `paid_transactions_seen`      | Shows whether previously returned transactions were later resolved.                 | Shows that past payment issues were actively addressed and resolved, which may reduce the concern associated with previous returns. | `true` = past issues resolved.                                                                                                                                                      | Boolean, `true` / `false`                                                                                                                   |
| `phone_number_present`        | Indicates a phone number is associated with the account.                            | Adds another identity signal.                                                                                                       | `true` = slightly stronger identity confidence.                                                                                                                                     | Boolean, `true` / `false`                                                                                                                   |
| `account_duplicate`           | Shows whether the account has been seen before in GrailPay's network.               | Prior history provides additional context.                                                                                          | `true` = existing account history.                                                                                                                                                  | Boolean, `true` / `false`                                                                                                                   |
| `taxpayer_number_match`       | Compares the provided SSN/EIN with records associated with the account.             | Strong identity verification signal.                                                                                                | `yes` = strong match. `close_match` = minor discrepancy. `no` = significant concern. `not_available` = comparison unavailable.                                                      | String: `yes`, `no`, `close_match`, `not_available`. Returned only when `taxpayer_number_match` is enabled in the request `actions` object. |

***

## Feedback

Account Intelligence is a continuously improving system, and its accuracy depends on real-world outcome data from your
integration. The Feedback endpoint allows you to report return events that occurred despite receiving an acceptable risk
score, closing the loop between prediction and outcome.

Submitting feedback is a critical part of getting the most out of Account Intelligence. Feedback data is used for
analytics, model evaluation, and ongoing training — directly improving the accuracy of future risk scores across your
account portfolio. Integrations that consistently submit feedback benefit from more precise, tailored risk assessments
over time.

<Info>
  We strongly recommend incorporating feedback submission into your standard return-handling workflow. Consistent
  feedback is the single most effective way to improve the accuracy of your Account Intelligence results.
</Info>

For complete request and response definitions, visit the [Account Intelligence Feedback OpenAPI Documentation](https://risk-api.grailpay.com/spec/#/Account%20Feedback/post_api_v3_accounts_feedback).

### When to Submit Feedback

You should submit feedback when:

* A transaction results in an unexpected return despite receiving a low-risk Account Intelligence score
* You want to improve the accuracy of future risk evaluations for your account portfolio
* You need to report false negatives for internal tracking and model monitoring

### Request Fields

When submitting feedback, your request payload should include details about the account, the return event, and a
reference to the original Account Intelligence evaluation. Required fields ensure we can accurately link your feedback
to the original prediction, while optional fields provide additional context that strengthens model analysis.

#### Required Fields

The following fields are required to link your feedback to the original Account Intelligence evaluation and the
associated return event.

| Field                  | Type    | Description                                                                   |
| ---------------------- | ------- | ----------------------------------------------------------------------------- |
| `account_number`       | string  | The account number from the original evaluation                               |
| `routing_number`       | string  | The routing number from the original evaluation                               |
| `outcome_status`       | string  | The outcome status of the transaction. Possible values: `settled`, `returned` |
| `return_code`          | string  | The ACH return code received (e.g., `R01`, `R02`, `R03`)                      |
| `name_match_requested` | boolean | Whether name matching was enabled in the original request                     |

Additionally, you must include **one** of the following to link to the original prediction:

| Field                  | Type      | Description                                                                                   |
| ---------------------- | --------- | --------------------------------------------------------------------------------------------- |
| `inference_request_id` | UUID      | The request ID returned by GrailPay in the original Account Intelligence response (preferred) |
| `inference_timestamp`  | timestamp | The UTC timestamp of the original request, if the request ID is unavailable                   |

<Note>
  Providing the `inference_request_id` is strongly recommended as it ensures accurate linkage to the original risk
  evaluation.
</Note>

#### Optional Fields

The following fields are not required but provide additional context that strengthens model analysis when available.

| Field                      | Type      | Description                                                 |
| -------------------------- | --------- | ----------------------------------------------------------- |
| `returned_at`              | timestamp | When the return was received                                |
| `transaction_initiated_at` | timestamp | When the transaction was initiated (client-side)            |
| `sec_code`                 | string    | The SEC code of the transaction (e.g., `WEB`, `CCD`, `PPD`) |
| `amount`                   | decimal   | The transaction amount                                      |
| `direction`                | string    | Transaction direction: `credit` or `debit`                  |

***

## Usage Recommendations

* Always pass complete and accurate identity data to maximize result quality.
* Use the `confidence_score` and `decisioning_insights` to guide onboarding and risk workflows.
* Review fields such as `name_match`, `valid_routing_number`, and `negative_transactions_seen` closely when the score is low.
* Submit feedback promptly after a return event occurs to ensure accurate timestamps and traceability.
* Always include the `inference_request_id` when available for precise linkage to the original prediction.
* Provide optional fields like `amount`, `sec_code`, and `direction` when possible to enrich the feedback data.

***

## Questions?

If you're encountering any issues, please reach out to [support@grailpay.com](mailto:support@grailpay.com).

***
