Account Intelligence
Overview
Section titled “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.
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.
Versions
Section titled “Versions”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, with 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
Section titled “Standard”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.
Sandbox Testing
Section titled “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
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.
There Are No Test Account Numbers
Section titled “There Are No Test Account Numbers”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.
Standard Test Routing Numbers
Section titled “Standard Test Routing Numbers”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 |
Lite Test Routing Numbers
Section titled “Lite Test Routing Numbers”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.
Errors and Other Routing Numbers
Section titled “Errors and Other Routing Numbers”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 |
Actions
Section titled “Actions”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,liteorstandard. Defaults tostandardwhen 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.
Response Attributes
Section titled “Response Attributes”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 and a supplemental_data object instead. Standard responses do not include valid or supplemental_data;
both are specific to Lite.
Confidence Score
Section titled “Confidence Score”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 |
Decisioning Insights
Section titled “Decisioning Insights”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. |
Lite Verdict
Section titled “Lite Verdict”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. |
Lite Supplemental Data
Section titled “Lite Supplemental Data”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.
Feedback
Section titled “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.
For complete request and response definitions, visit the Account Intelligence Feedback OpenAPI Documentation.
When to Submit Feedback
Section titled “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
Section titled “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
Section titled “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 |
Optional Fields
Section titled “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
Section titled “Usage Recommendations”- 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_scoreanddecisioning_insightsto guide onboarding and risk workflows. - Review fields such as
name_match,valid_routing_number, andnegative_transactions_seenclosely when the score is low. - On Lite responses, match on the supplemental
coderather than itsdescription, 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_idwhen available for precise linkage to the original prediction. - Provide optional fields like
amount,sec_code, anddirectionwhen possible to enrich the feedback data.
Questions?
Section titled “Questions?”If you’re encountering any issues, please reach out to support@grailpay.com.
Maintained by EkLine