Skip to content
Select theme

Risk Intelligence API Changelog

Every operation in the Risk Intelligence API reference now has a short title, with the fuller explanation moved into the operation’s description.

The reference reads as a list of operations, such as “Validate account”, “Get access token” and “Create guarantee”, rather than a list of sentences, and the detail that used to sit in the title is still there as the description beneath it.

  • Titles are a few words long, for example “Validate account”, “Submit account feedback” and “Cancel guarantee”; the previous sentence-length titles become each operation’s description
  • The API itself is unchanged: this release touches the OpenAPI document only

Account Intelligence Lite responses now carry a live flag that tells a live result from a test one, and Lite and Standard requests are billed separately.

live shows an integrator whether a result came from the live production path, which a reserved test routing number or a sandbox call never does, so test traffic can be told apart from real traffic in the client’s own records. Billing by version means a client is charged for the version it requests.

  • Lite 200 responses from POST /api/v3/accounts/validate include live: in production, true for a real routing number and false for a Lite test number; in the sandbox and every other environment, always false. Standard responses and error responses do not include it
  • Standard requests on v3 and v2 are billed as Standard validations and Lite requests as Lite validations; test routing numbers, 999999999 and requests that never reach a data provider are not billed, and name match and taxpayer number match billing is unchanged
  • The Lite test routing numbers, 999999999 and all Standard behavior are unchanged

POST /api/v3/accounts/validate gains an Account Intelligence Lite version. Sending actions.version: lite returns a Lite response, a simple valid verdict with supplemental reason codes, instead of a confidence score and decisioning insights.

Lite is a lighter validation for integrators that need a likely, unlikely or indeterminate answer rather than a score and insights. Its test routing numbers work in production as well as in the sandbox, so an integration can be verified end to end in either environment. Requests that omit actions.version are unchanged.

  • actions.version accepts lite or standard; omitting it means standard, and any other value returns 422. A Lite request with actions.name_match or actions.taxpayer_number_match set to true returns 422
  • A Lite response carries valid (likely, unlikely or indeterminate) and supplemental_data.codes, an array of {code, description} objects that is empty when no code applies; it never carries confidence_score or decisioning_insights
  • Lite test routing numbers, valid in both sandbox and production: 000000100 likely; 000000101 unlikely, invalid input format; 000000102 unlikely, invalid routing number; 000000103 unlikely, invalid account number format; 000000104 unlikely, account number not eligible for ACH; 000000106 indeterminate, no account information found; 000000107 unlikely, potential risk indicator; 000000108 likely, frequently observed active account; 999999999 simulated service outage
  • The OpenAPI document shows both the Standard and the Lite response

POST /api/v3/accounts/validate now honors X-Idempotency-Key per client and per request body: the first response for a key is stored, a retry with the same key and body returns it, and the same key with a different body is refused. The guarantee feedback endpoint is now documented in the OpenAPI document.

A client that retries an account validation after a timeout receives the original result and is not billed a second time, including for name match and taxpayer number match, and a key cannot be reused for a different account by mistake. Generated clients can now be built for POST /api/v3/guarantees/{guarantee_id}/feedback from the published document.

  • On POST /api/v3/accounts/validate, the stored response for an X-Idempotency-Key is scoped to the authenticated client and the request body; a retry with the same key and body replays the first response and records no additional billing
  • The same key with a different body returns 409 with {"error": "Idempotency key reused with a different request body"}; requests without the header behave as before
  • POST /api/v3/guarantees/{guarantee_id}/feedback is documented in the OpenAPI document with its request, header and error contract

The OpenAPI document now describes the Account Intelligence v3 response in full: every decisioning_insights field is listed with its type, allowed values and an example, and the request documents the bank_account, actions and location objects clients send.

Generated clients and validators built from the document will match real POST /api/v3/accounts/validate responses, where the insights object was previously shown as empty, and the request schema shows the nested shape the API accepts.

  • The 200 response schema for POST /api/v3/accounts/validate lists each decisioning_insights field with types, enums (including name_match and taxpayer_number_match) and example values, alongside confidence_score
  • The request schema documents bank_account, actions and location (address, ip_address and gps)

Guarantee feedback is now submitted against the guarantee’s id: POST /api/v3/guarantees/{guarantee_id}/feedback replaces POST /api/v3/transactions/guarantees/feedback, which now returns 404. The body no longer carries inference_request_id, and the response returns guarantee_id.

This is a breaking change for feedback integrations, and it completes the move to one identity for a guarantee: the id from the create response is the key for both cancellation and feedback. Feedback is accepted for both guarantee and no_guarantee decisions, and a cancelled guarantee still accepts feedback, since outcome_status: "cancelled" describes the transaction rather than the guarantee.

  • POST /api/v3/guarantees/{guarantee_id}/feedback takes the same event fields as before (client_reference_id, client_originated, client_guarantee_applied, outcome_status, outcome_observed_at, noc_received and the conditional return and NOC fields) and a unique X-Idempotency-Key per event; inference_request_id is not accepted in the body
  • 200 returns guarantee_id, client_reference_id, status (recorded for the first event on a guarantee, updated for later ones) and received_at
  • 409 for a reused X-Idempotency-Key or an event whose client_originated or client_guarantee_applied differs from the first event; 404 for an unknown guarantee_id, another client’s guarantee or one without a decision; 422 for validation errors, including client_guarantee_applied: true on a no_guarantee decision
  • POST /api/v3/transactions/guarantees/feedback returns 404

The sandbox test routing numbers now work on the receivable guarantee endpoint, and the cancellation endpoint is documented in the OpenAPI document.

A sandbox integration can obtain a guarantee and a no_guarantee decision deterministically, and exercise the unavailable path, without depending on live decisioning rules. A guarantee created with the approved test number has a real id, so cancellation can be tested against it too. Any other routing number takes the live path, and production never stubs.

  • In the sandbox, POST /api/v3/guarantees/receivable with payer.bank_account.routing_number 000000001 returns decision: guarantee with a cancellable id, 000000002 returns decision: no_guarantee, and 999999999 returns 503 with the message “Service temporarily unavailable. Guarantee decisioning is currently experiencing a disruption. Please retry your request later.”
  • The request is validated in full before a test number is matched, and any other valid routing number is evaluated live
  • POST /api/v3/guarantees/{guarantee_id}/cancel is documented in the OpenAPI document with its 200, 401, 404 and 409 responses

The guarantee API has a new request contract and new paths. A receivable guarantee is created with POST /api/v3/guarantees/receivable, the create response returns the guarantee’s id alongside the decision, and that id is what a cancellation now references: POST /api/v3/guarantees/{id}/cancel. The former POST /api/v3/transactions/guarantees path returns 404.

This is a breaking change for every guarantee integration. The request is reorganized around payer, payee and transaction with nested bank accounts, tax_id in place of the SSN and EIN fields, line_1 and postal_code address fields, and the amount in cents; a request in the old shape is rejected. Clients must store the id from the create response to cancel, since cancellation by request ID is gone. Feedback continues to work for guarantees created on the old path.

  • POST /api/v3/guarantees/receivable takes payer, payee and transaction and returns 200 with id (a UUID) and decision (guarantee or no_guarantee)
  • payer carries bank_account (account_number, routing_number); exactly one of individual (first_name, last_name, email, phone; middle_name, prefix, suffix and tax_id optional) or organization (name, alternate_name, email, phone; tax_id optional); location with ip_address and an address (line_1, city, state, postal_code, country_code; line_2 and line_3 optional); and the behavioral fields account_opened_at, user_total_spent_prior (cents), user_transaction_count_prior, is_first_time_user, device_id, source (mobile_app_ios, mobile_app_android, web, subscription or other) and as_of_timestamp
  • payee carries bank_account, organization (business_name, trading_name, email, phone, tax_id; incorporation_state, industry and business_email optional), location in the same shape as the payer’s, and beneficial_owners with at least one owner (first_name, last_name, email, phone, dob, tax_id, address; middle_name, prefix, suffix, is_account_owner and is_significant_control_person optional)
  • transaction carries amount in cents (greater than zero), sec_code (WEB, CCD or PPD) and created_date; speed (standard or same_day, default standard) and client_reference_id are optional. An amount of $10,000.00 or more is answered no_guarantee
  • POST /api/v3/guarantees/{id}/cancel cancels a guarantee with decision: guarantee within 15 minutes of issuance; 404 for an unknown id, another client’s guarantee or a no_guarantee decision, and 409 when already cancelled or outside the window
  • POST /api/v3/transactions/guarantees and cancellation by request ID are removed and return 404

Request validation now behaves the same way on every endpoint. A request that fails validation returns 422 with a message and an errors map keyed by field, and the rules behind it have been aligned across account validation, account feedback, verify account and routing, the transaction risk score and the guarantee endpoints. The party object on the transaction risk score and guarantee requests is payor only, a guarantee can be cancelled only within 15 minutes of being issued, and GET / on the API host now returns a small JSON status payload.

Integrators that parse validation errors should expect one shape everywhere: HTTP 422, a top-level message, and errors as an object whose keys are field paths such as payor.address.zip and whose values are arrays of messages. Malformed values are rejected before any data provider is called, with the offending field named. The payer spelling is not accepted as an alias for payor, and a cancellation attempted more than 15 minutes after the guarantee was issued returns 409.

  • Validation failures on every endpoint return 422 with {"message": "...", "errors": {"field": ["..."]}}; v1 and v2 verify account and routing requests with a non-numeric account number or a routing number that is not exactly 9 digits now fail validation rather than returning a result
  • POST /v1/transactions/score/ar and POST /api/v3/transactions/guarantees accept the party object as payor only; a body that sends payer is rejected
  • Phone numbers must be exactly 10 digits, ssn9 values must be a valid SSN or ITIN, routing numbers exactly 9 digits, and postal codes 5 digits, 9 digits or ZIP+4 (5 digits on the transaction risk score); these are checked before any provider is called
  • POST /api/v3/accounts/validate validates location.ip_address, requires location.gps coordinates, when present, to fall within the continental United States, and requires an identity.individual or identity.organization object, when present, to carry its fields
  • POST /api/v3/accounts/feedback accepts only returned, unexpected_return or other as outcome_status
  • On POST /api/v3/transactions/guarantees, payee.beneficial_owners is required (it may be empty) and each owner’s ssn9 is validated as an SSN or ITIN
  • POST /api/v3/transactions/guarantees/feedback requires inference_request_id to be a UUID, requires outcome_observed_at, returned_at and noc_received_at to carry a timezone, and validates the return and NOC fields against outcome_status and noc_received
  • POST /api/v3/transactions/guarantees/{request_id}/cancel succeeds only within 15 minutes of the guarantee being issued; later attempts return 409
  • GET / on the API host returns {"status": "ok", "documentation": "...", "request_id": "..."}, with the request ID also in the X-RI-Request-ID header

Transaction guarantees now have a hard amount limit. A guarantee request for a transaction strictly above $10,000.00 is answered no_guarantee without being sent to the decisioning service; a transaction of exactly $10,000.00 is still evaluated. Separately, the three guarantee endpoints have been removed from the public OpenAPI document served at /spec and /spec-api; they remain available and unchanged.

Integrators submitting large transactions should expect no_guarantee above the limit rather than a decision based on the transaction’s risk. The limit is a placeholder that GrailPay expects to revise. Tooling that reads the OpenAPI document will stop seeing the guarantee, cancel and feedback endpoints, so generated clients for those endpoints should be kept rather than regenerated.

  • POST /api/v3/transactions/guarantees returns no_guarantee for any transaction.amount strictly above $10,000.00; the request’s provider data is still collected and retained, and amounts of exactly $10,000.00 are evaluated as before
  • /api/v3/transactions/guarantees, /api/v3/transactions/guarantees/feedback and /api/v3/transactions/guarantees/{request_id}/cancel and their schemas are no longer in the public OpenAPI document; the endpoints themselves are unchanged

The guarantee request’s payer object is now validated strictly: every payer field is required apart from a few optional identity fields, unknown keys are rejected, and timestamps must carry a timezone. When a data provider the guarantee depends on is unavailable, the endpoint answers with an error and no decision. On account validation, the identity details passed to the matching provider are limited to what the requested actions need, and name fields are trimmed of surrounding whitespace, with whitespace-only names rejected.

A guarantee decision depends on a complete, well-formed payer profile, so the contract now fails loudly on a missing or misspelled field instead of degrading the decision silently. Integrators calling the guarantee endpoint should check their payloads against the required list below before upgrading. A 500 from the guarantee endpoint during a provider outage carries no partial or fabricated decision; the request should be retried later. Account validation requests that only ask for name_match no longer have their SSN, contact details or address used for the name check, which makes name_match results depend on the name alone.

  • On POST /api/v3/transactions/guarantees, payer requires name (2–100 characters), email, phone, account_number, routing_number, address (street_1, street_2, city, state, zip), identity, location (address and ip_address) and metadata with all six of its fields; only identity.individual.ssn, middle_name, prefix and suffix and identity.organization.ein are optional
  • identity must contain exactly one of individual (first_name, last_name, email, phone_number, device_id) or organization (name, alternate_name, email, phone_number, device_id)
  • An unknown or misspelled key anywhere in payer returns 400 naming the key; metadata.account_opened_at and metadata.as_of_timestamp must be ISO-8601 timestamps with an explicit offset
  • When a required data provider cannot be reached during a guarantee evaluation, the endpoint returns 500 with a “service temporarily unavailable, please try again later” message and no decision
  • For POST /api/v3/accounts/validate, a name_match request sends only the name to the identity provider; taxpayer_number_match adds the SSN or EIN; contact details and address are not sent in either case
  • Leading and trailing whitespace is trimmed from name fields (payor.name, the individual’s first_name, last_name, middle_name, prefix and suffix, the organization’s name and alternate_name, and the payee’s business_name, trading_name and beneficial owner names), and a value that is only whitespace is rejected

Authentication failures now return one uniform response, 401 with {"message": "Unauthorized"}, whether the token is missing, malformed, expired or revoked. The token endpoints are rate limited per client, every response carries a standard set of security headers, and provider results used for guarantee decisions are reused for up to 15 days.

Clients that branch on authentication errors can rely on one status code and one message key: message is the key every other error on the API uses. POST /v1/auth and POST /v1/auth/refresh reject bursts of requests with 429 and a Retry-After header, so a token should be obtained once and reused rather than requested per call. For guarantees, a repeat request for the same payer within 15 days reuses the provider data gathered the first time, while account validation keeps its 90-day window.

  • Every authentication failure returns 401 with {"message": "Unauthorized"}; the detailed reason is kept in GrailPay’s logs only
  • POST /v1/auth allows 10 requests per minute and POST /v1/auth/refresh 20 per minute from one client address; requests over the limit receive 429 with Retry-After
  • Responses include Strict-Transport-Security, X-Content-Type-Options, Content-Security-Policy, X-Frame-Options, Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy and Cross-Origin-Resource-Policy headers
  • Provider results gathered for a guarantee decision are cached for 15 days; results gathered for account validation keep their 90-day cache

Clients can now report what they did with a guarantee decision and how the transaction turned out. A new POST /api/v3/transactions/guarantees/feedback endpoint records, per guarantee request, whether the transaction was originated, whether GrailPay’s guarantee was applied, and the observed outcome, including return and Notification of Change details.

Feedback closes the loop between a decision and the real-world result, and helps GrailPay measure and improve its decisions and reconcile billing. A transaction can produce several feedback events over its life, for example settled and later returned, each sent as its own event. The original decision is read from GrailPay’s own record of the request, so the client never sends it. Feedback does not change real-time decisioning.

  • POST /api/v3/transactions/guarantees/feedback requires the X-Idempotency-Key header, one unique key per feedback event, and a body with inference_request_id (the guarantee request’s ID), client_reference_id, client_originated, client_guarantee_applied, outcome_status, outcome_observed_at and noc_received
  • outcome_status is one of not_originated, originated, settled, returned, cancelled or failed_processing; a returned outcome also requires return_code and returned_at
  • When noc_received is true, noc_code and noc_received_at are required and noc_reason is optional
  • 200 returns inference_request_id, client_reference_id, received_at and status: recorded for the first event for a guarantee request, updated for each later one
  • 400 for a validation error, an unknown field, a timestamp without a timezone, or a missing idempotency key; 404 for a request ID with no prior guarantee by this client; 409 for a reused idempotency key or a later event that contradicts an earlier one; 503 if the event could not be stored

The taxpayer_number_match result on Account Intelligence v3 can now be close_match, and a new sandbox routing number exercises it. The guarantee cancellation response is simplified: the cancelled guarantee’s ID is returned as cancelled_request_id, and the status field is removed.

close_match reports a partial taxpayer identifier match that was previously reported as not_available, so an integrator that switches on taxpayer_number_match needs a case for it. The cancellation change is breaking for code that reads request_id or status from the cancel response body: the per-call request ID is in the X-RI-Request-ID header, and cancelled: true is the confirmation.

  • decisioning_insights.taxpayer_number_match on POST /api/v3/accounts/validate accepts a fourth value, close_match, alongside yes, no and not_available; a value the API does not recognize is reported as not_available
  • Sandbox routing number 000000004 returns 200 with confidence_score: 0.65, taxpayer_number_match: "close_match", name_match: "not_available" and name_match_score: 0.0; the other sandbox numbers are unchanged
  • POST /api/v3/transactions/guarantees/{request_id}/cancel returns cancelled_request_id, cancelled and cancelled_at; the body does not include request_id or status

In the sandbox, every transaction guarantee request is now evaluated by the live decisioning service and returns a real decision; the guarantee endpoint is not stubbed for any routing number. The sandbox documentation for Account Intelligence v3 now lists the name_match_score each test routing number returns.

A sandbox integration of the guarantee endpoint exercises the same decisioning path as production, even when a request uses the account validation test routing numbers, so what a client sees in the sandbox is a genuine decision. The account validation test numbers keep the same outcomes as before, and the score each one returns is now documented.

  • POST /api/v3/transactions/guarantees is not stubbed in the sandbox: any request with all required fields is routed to the live guarantee decisioning service
  • Sandbox name_match_score values for POST /api/v3/accounts/validate: 0.98 for 000000001, 0.0 for 000000002 and 0.14 for 000000003; taxpayer_number_match for those numbers is yes, not_available and no

Transaction guarantees are available on the Risk Intelligence API. POST /api/v3/transactions/guarantees evaluates a payer, a payee and a transaction and answers with a single decision, guarantee or no_guarantee, and POST /api/v3/transactions/guarantees/{request_id}/cancel withdraws a guarantee that was issued.

A guarantee decision means GrailPay takes on the balance sheet risk for that transaction, and cancelling the guarantee removes it. The response is deliberately minimal: it carries no scores, provider output or decisioning insights, and the request ID an integrator needs for a later cancellation comes from the X-RI-Request-ID response header rather than the body.

  • POST /api/v3/transactions/guarantees takes payer, payee and transaction objects and returns 200 with { "decision": "guarantee" } or { "decision": "no_guarantee" }; the request’s ID is in the X-RI-Request-ID header, and the optional X-Idempotency-Key header is supported
  • payer requires account_number and routing_number; name, email, phone (10 digits), address, identity, location and a metadata object are optional. metadata carries client-side signals about the payer: account_opened_at, user_total_spent_prior (in cents), user_transaction_count_prior, is_first_time_user, source and as_of_timestamp
  • payee accepts an optional business.email, a business email distinct from the contact email
  • transaction requires amount (in dollars, a decimal number greater than zero), sec_code (WEB, CCD or PPD), direction (debit or payout) and date; speed (standard or same_day) and client_reference_id are optional
  • A validation error returns 400 with field-level errors, and 503 is returned when the decisioning service is unavailable
  • POST /api/v3/transactions/guarantees/{request_id}/cancel returns 200 with request_id, status: "cancelled", cancelled: true and cancelled_at; a guarantee that is already cancelled returns 409, and an unknown request ID, including one issued to another client, returns 404
  • In the sandbox at this release, a payer routing number of 000000002 returns a declined guarantee, 999999999 returns service unavailable, and any other valid routing number returns an approved guarantee

The sandbox test routing numbers for Account Intelligence v3 now work on the v1 and v2 endpoints as well, returning each version’s own response shape, and they are documented on those endpoints in the OpenAPI document.

Integrators choose v1, v2 or v3 of Account Intelligence through the Payments API, so the same test numbers now produce a known outcome whichever version is in use, and a sandbox test can cover every supported version consistently.

  • POST /v1/verify-account-routing in the sandbox: 000000001 returns 200 with result: "valid", 000000002 with result: "invalid", 000000003 with result: "not_validated"; an empty or missing routing_number returns 400, and 999999999 returns 503
  • The v2 verify account and routing endpoint accepts the same numbers: 000000001 returns 200 with risk_score: 0.90 and confidence_level: "very_high", 000000002 with risk_score: 0.55 and confidence_level: "medium", 000000003 with risk_score: 0.15 and confidence_level: "very_low"

The Account Intelligence v3 response now includes decisioning_insights.name_match_score, a normalized name match confidence between 0.0 and 1.0. The API also serves browser-based clients: cross-origin requests receive CORS headers and preflight OPTIONS requests are answered, so the API can be called from the OpenAPI document’s “Execute” control and from other browser-side tooling.

name_match_score gives the degree of confidence behind the yes, no or not_available in name_match, so an integrator can apply its own threshold. It is only meaningful when a name match was requested: otherwise name_match is not_available and the score is 0.0. With CORS in place, a request made from a browser receives the body, status and headers the same way a server-side call does.

  • decisioning_insights.name_match_score is a float from 0.0 to 1.0, rounded to two decimals; it is 0.0 when name match was not requested or no score is available, and the OpenAPI document describes it
  • name_match and name_match_score are populated only when actions.name_match is requested; otherwise name_match is not_available
  • Preflight OPTIONS requests are answered, the Authorization, Content-Type and X-Idempotency-Key request headers are allowed, and X-RI-Request-ID, Retry-After and the rate limit headers are exposed to browser clients

The Account Intelligence v3 response now reports the taxpayer number match result. A new decisioning_insights.taxpayer_number_match field uses the same yes, no and not_available values as name_match, and name_match itself is now limited to those three values.

An integrator that requests taxpayer_number_match can read the outcome from the response next to the name match result, and both fields have a closed set of values to switch on. A match that was not requested, or for which no data is available, is reported as not_available rather than omitted, in the sandbox as well as in production.

  • decisioning_insights.taxpayer_number_match is included in the v3 account validation response, with values yes, no or not_available
  • decisioning_insights.name_match is standardized to yes, no or not_available
  • Both fields are not_available when the matching data is unavailable
  • In the sandbox, the test routing numbers return taxpayer_number_match of yes for 000000001, not_available for 000000002 and no for 000000003, and a positive match is returned only when the corresponding action was requested

The tin_match action on Account Intelligence v3 is renamed taxpayer_number_match, so that the name covers Social Security numbers as well as EINs. The OpenAPI document now gives POST /api/v3/accounts/validate an explicit 429 Too Many Requests response with a Retry-After header, and shows separate request examples for an individual and for an organization identity.

An integration sending actions.tin_match must switch to actions.taxpayer_number_match; the rest of the request is unchanged, and identity.individual.ssn or identity.organization.ein is still required when it is true. A rate-limited request should be retried after the interval in Retry-After, which the API preserves on every 429. The split examples make clear that a request carries exactly one of identity.individual and identity.organization.

  • actions.tin_match is replaced by actions.taxpayer_number_match on POST /api/v3/accounts/validate; identity.individual.ssn or identity.organization.ein is required when it is true
  • POST /api/v3/accounts/validate documents a 429 response carrying Retry-After and X-RI-Request-ID, and a rate-limited response keeps the Retry-After value the API sets
  • The OpenAPI document shows an individual-identity and an organization-identity request example, since only one of the two identity objects may be sent

The sandbox test routing numbers for POST /api/v3/accounts/validate are now documented in the OpenAPI document. In the sandbox environment the outcome of a v3 account validation depends on the bank_account.routing_number sent, so each result can be exercised deliberately.

A sandbox integration can be tested against a known low-risk, medium-risk and high-risk account, a validation error and an unavailable upstream without guessing which values produce which response, and the name match result for each test number is predictable.

  • 000000001: low risk, full match; 200 with confidence_score: 0.91 and decisioning_insights.name_match: "yes"
  • 000000002: medium risk, name not available; 200 with confidence_score: 0.53 and decisioning_insights.name_match: "not_available"
  • 000000003: high risk, negative history; 200 with confidence_score: 0.12 and decisioning_insights.name_match: "no"
  • An empty or missing routing_number returns 400 with validation errors, and 999999999 returns 503 for an unavailable upstream

Account Intelligence v3 gains a tin_match action that matches a taxpayer identifier for the account holder: a Social Security number for an individual, or an EIN for an organization. Every response now carries an X-RI-Request-ID header, and the OpenAPI document lists the sandbox and production hosts.

With tin_match, a single account validation request can confirm that the taxpayer identifier on file belongs to the account holder, and one billable event is recorded for it whether the identifier is an SSN or an EIN. The request identifier in X-RI-Request-ID is what to quote when asking GrailPay about a specific call.

  • POST /api/v3/accounts/validate accepts actions.tin_match; when it is true, identity.individual.ssn or identity.organization.ein is required, and a request without the identifier is rejected with a validation error
  • identity.individual.ssn and identity.organization.ein are new request fields; tin_match records one billable event per request regardless of identifier type, and requests without tin_match behave as before
  • Every response includes an X-RI-Request-ID header identifying the request, now documented in the OpenAPI document, which also lists the sandbox and production server URLs

Account Intelligence v2 and v3 now return a service-unavailable response when the risk model cannot produce a valid confidence score for a request.

A request that cannot be scored is reported as an error rather than as a result, so an integrator can retry it or route it for manual handling instead of treating an empty score as an outcome.

  • A missing or non-numeric confidence score from the risk model is answered with a service-unavailable response on the v2 and v3 account validation endpoints

The API’s OpenAPI document, served at /spec, is now OpenAPI 3.0.3.

Tooling that reads the document, such as client generators and validators, should expect the OpenAPI 3 format. Bearer authentication is declared once, and request and response schemas are defined once as shared components rather than repeated under each endpoint.

  • The OpenAPI document is OpenAPI 3.0.3, with centralized servers, bearer authentication and shared request and response schema components

The Account Intelligence feedback endpoint now validates account_number and routing_number with the same rules as the validation endpoints, and the v2 account and routing verification endpoint reports an invalid account or routing number with a clearer message.

A feedback submission with an account number longer than 17 characters is rejected, so integrators should send the account number as it was validated. Clients that match on the v2 error text should expect “Invalid account or routing number.”

  • POST /api/v3/accounts/feedback limits account_number to 17 characters and validates account_number and routing_number with the shared account and routing number rules
  • The v2 verify account and routing endpoint returns the message “Invalid account or routing number.” for an account or routing number that does not pass validation

Clients can now report the real-world outcome of a transaction whose account received an acceptable Account Intelligence score. A new POST /api/v3/accounts/feedback endpoint records the account, the outcome and the return code, linked to the original validation request.

Feedback closes the loop between a score and what happened afterward. GrailPay uses it to monitor model performance and improve risk accuracy over time; it is used for analytics, evaluation and training, not to change the score already returned. Only clients integrated with the Risk Intelligence API can submit feedback, and each record is tied to the authenticated client.

  • POST /api/v3/accounts/feedback requires account_number, routing_number, outcome_status, return_code and name_match_requested, plus either inference_request_id (the UUID GrailPay returned) or inference_timestamp to identify the original validation
  • Optional fields: returned_at, transaction_initiated_at, sec_code, amount and direction (credit or debit)
  • outcome_status accepts returned, unexpected_return or other
  • A valid submission is answered with 200 and a confirmation message; submissions are validated, logged and persisted
  • The endpoint is documented in the OpenAPI document with its request and response schemas

State-changing requests to the Risk Intelligence API can now be made idempotent. Sending an X-Idempotency-Key header on a POST request stores the response for about 24 hours, and a repeat of the same request with the same key returns the stored response instead of being processed again. The API also now reuses results from its underlying data providers for repeated identical requests.

A client that retries after a timeout or a dropped connection can do so safely: the retry returns the original response and does not create a second resource or trigger a second evaluation. Reusing provider results means an identical validation request made again within the retention window is answered from the earlier provider result rather than a new call.

  • Optional X-Idempotency-Key header on POST endpoints such as /api/v3/accounts/validate and /v1/transactions/score/ar; the first response is stored for up to 24 hours and later requests with the same method, path and key receive it without reprocessing
  • Reusing a key on a different endpoint is rejected with 409 Conflict
  • Responses larger than 1 MB and 5xx responses are not stored, so those requests are processed again on retry; only a safe subset of response headers, such as Content-Type, is replayed
  • A request without the header is processed normally, and a key that has expired is treated as new
  • Results from the API’s data providers are cached and reused for identical requests for up to 90 days
  • The OpenAPI document describes the idempotency header and the 409 response

  • OpenAPI Documentation Updates: Added all the necessary changes to accurately document the Transaction Intelligence endpoint.

  • OpenAPI Documentation Updates:
    • Added all the necessary changes to accurately document the V3 Account Intelligence endpoint.
    • Marked all of the V1 and V2 Account Intelligence endpoints as deprecated.

Maintained by EkLine