Skip to content
Select theme

Account Intelligence

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.

Account Intelligence is a single endpoint offered in two versions, Lite and Standard. Lite returns a fast validity verdict from the account and routing numbers alone. Standard, the default, returns a score and the explainable insights behind it. You choose the version in the request body, not by the path you call. See Versions.

With a single API call, Standard 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 a Standard 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 on Standard requests. More complete input data results in more meaningful confidence scores and more actionable insights.

For complete request and response definitions, visit the Account Intelligence OpenAPI Documentation.


Both versions of Account Intelligence are served by the same endpoint, POST /api/v3/accounts/validate. Which one you receive is set by version in the actions object of the request body. A request that omits version resolves to Standard.

Lite Standard (default)
actions.version lite standard, or omitted
Input Account number and routing number Account and routing numbers required; full identity data recommended
Output A valid verdict of likely, unlikely, or indeterminate, a live indicator, and supplemental_data A confidence_score from 0.00 to 1.00 and decisioning_insights
Best for A fast, low-friction validity signal with no identity data Composable insights that can drive tailored workflows

Lite answers one question: is this account likely valid? It runs checks on the routing and account numbers across GrailPay’s data sources and returns a categorical verdict. No identity data is required.

To request Lite, set version to lite in the actions object:

{
"actions": {
"version": "lite"
}
}

Send version on its own. Lite does not accept identity data or the name_match and taxpayer_number_match actions, and a Lite request that includes them is rejected with a 422 validation error.

Standard returns a calibrated confidence score plus explainable decisioning insights from a single API call. Account and routing numbers are required, and supplying full identity data yields more meaningful results. Standard also supports the optional name_match and taxpayer_number_match actions.

Standard is the version you receive when version is set to standard or left out of the request.


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

Each Version Has Its Own Test Routing Numbers

Section titled “Each Version Has Its Own Test Routing Numbers”

Lite and Standard each reserve their own routing numbers, and a reserved number returns its fixed result only on a request for the version it belongs to. Sent to the other version, it is not a test scenario, and the request is processed as it would be for any other routing number.

For example, 000000001 returns the low-risk, full-match result on a Standard request. On a request with actions.version set to lite, the same routing number is processed as a regular Lite request.

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

For example, on a Standard request, 000000001 paired with any valid account number returns the full-match result. Pairing that same account number with 000000003 returns the high-risk result. Lite works the same way: 000000100 paired with any valid account number returns a likely verdict.

These routing numbers return a fixed result on Standard requests, meaning actions.version is set to standard or omitted.

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

Each one 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

These routing numbers return a fixed result on requests with actions.version set to lite. Each one returns a 200 with the valid verdict and supplemental_data.codes entry shown. There is one routing number for each supplemental code Lite returns today, plus one for the no-code case.

Routing Number valid supplemental_data.codes
000000100 likely Empty
000000101 unlikely 01 Invalid Input Format
000000102 unlikely 02 Invalid Routing Number
000000103 unlikely 03 Invalid Account Number Format
000000104 unlikely 04 Account Number Not Eligible for ACH
000000106 indeterminate 06 No Account Information Found
000000107 unlikely 07 Potential Risk Indicator
000000108 likely 08 Frequently Observed Active Account

The last two digits of the routing number match the supplemental code it returns, and 000000100 returns none. Code 05 is not returned today, so it has no test routing number.

If your integration runs its own ABA routing number checksum validation before calling the API, use 122105278 to test it. The number passes the standard checksum, so you can exercise your validation and the API call together.

122105278 is also reserved for Lite, but only in the sandbox. Paired with any valid account number, such as 0000000001, it returns the same result as 000000107:

{
"valid": "unlikely",
"supplemental_data": {
"codes": [
{
"code": "07",
"description": "Potential Risk Indicator"
}
]
},
"live": false
}

In production, 122105278 is not a test routing number and is processed like any other. It is not reserved for Standard either, so a Standard request for it in the sandbox falls through to live sandbox decisioning.

These behave the same way on both versions.

Routing Number Scenario HTTP Status
empty / missing Validation-style errors 422
999999999 Upstream unavailable 503
Any other value Falls through to live sandbox decisioning live

By default, every call to this endpoint performs core validation checks on the routing and account numbers. The optional actions object in your request does two things: it selects the Account Intelligence version, and on Standard requests it enables additional validation layers that enrich the results further.

Currently, the following actions are supported:

  • version: Selects the Account Intelligence version, lite or standard. Defaults to standard when omitted. See Versions.
  • name_match: Compares the provided name in the identity object with the account holder’s name on file. Standard only.
  • taxpayer_number_match: Compares the provided taxpayer identifier in the identity object with the account holder’s tax ID on file. Standard only.

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

{
"actions": {
"version": "standard",
"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.


What the endpoint returns depends on the version you request. Standard 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. Lite returns a valid verdict, a live indicator, and a supplemental_data object instead. Standard responses do not include valid, live, or supplemental_data; all three are specific to Lite.

The confidence_score, returned by Standard, 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

The decisioning_insights object, returned by Standard, 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.

A Lite response returns valid, a categorical verdict on the account.

Value What It Means
likely The account is likely valid and safe to transact.
unlikely The account is unlikely to be valid; review before processing.
indeterminate There is not enough signal to determine validity.

A Lite response includes a boolean live field alongside valid and supplemental_data. It tells you whether the verdict came from live decisioning or from a fixed test response.

Value What It Means
true The verdict was produced by live decisioning on the account and routing numbers you sent.
false The verdict is a fixed test response from a reserved routing number, or the request ran outside production.

In production, a request for a reserved Lite routing number returns live: false with its fixed result, and every other routing number returns live: true. In the sandbox and other non-production environments, live is always false.

The live field appears only on Lite 200 responses. Standard responses and the 503 responses do not include it.

The supplemental_data object carries additional context about how the verdict was reached. It is always present on a Lite response, and its codes array is always present but may be empty, so you can iterate over it without a null check. Today a response carries at most one code.

Each entry in codes has a two-character code and a description. Match on the code value. The description is display text and may be reworded without notice. Ignore codes you do not recognize so that new codes cannot break your integration.

Code Description
01 Invalid Input Format
02 Invalid Routing Number
03 Invalid Account Number Format
04 Account Number Not Eligible for ACH
05 No Name Information Found
06 No Account Information Found
07 Potential Risk Indicator
08 Frequently Observed Active Account

Code 02 indicates a well-formed nine-digit routing number that does not belong to a recognized institution. Code 05 is reserved and is not returned today.

Codes 01 and 03 are returned on a 200 response, not as validation errors. A malformed request is rejected with a 422 before any check runs. These two codes cover input that passes request validation but that one of our data sources still judges malformed or invalid.


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.

For complete request and response definitions, visit the Account Intelligence Feedback OpenAPI Documentation.

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

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.

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

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

  • Use Lite when you need a fast validity signal and have no identity data, and Standard when you want a score and insights to drive your decisioning.
  • On Standard requests, 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.
  • On Lite responses, match on the supplemental code rather than its description, and ignore codes you do not recognize.
  • 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.

If you’re encountering any issues, please reach out to support@grailpay.com.


Maintained by EkLine