---
title: "Evaluate a planned ACH transaction"
method: POST
path: "/signal/evaluate"
tags: ["plaid"]
---

# Evaluate a planned ACH transaction

`POST /signal/evaluate`

Use `/signal/evaluate` to evaluate a planned ACH transaction to get a return risk assessment and additional risk signals.

Before using `/signal/evaluate`, you must first [create a ruleset](https://plaid.com/docs/signal/signal-rules/) in the Dashboard under [**Signal->Rules**](https://dashboard.plaid.com/signal/risk-profiles).

`/signal/evaluate` can be used with either Signal Transaction Scores or the Balance product. Which product is used will be determined by the `ruleset_key` that you provide. For more details, see [Signal Rules](https://plaid.com/docs/signal/signal-rules/).

Note: This request may have higher latency when using a Balance-only ruleset. This is because Plaid must communicate directly with the institution to request data. Balance-only rulesets may have latency of up to 30 seconds or more; if you encounter errors, you may find it necessary to adjust your timeout period when making requests.

## Request body

- SignalEvaluateRequest — SignalEvaluateRequest defines the request schema for `/signal/evaluate`
  - `client_id` string — Your Plaid API `client_id`. The `client_id` is required and may be provided either in the `PLAID-CLIENT-ID` header or as part of a request body.
  - `secret` string — Your Plaid API `secret`. The `secret` is required and may be provided either in the `PLAID-SECRET` header or as part of a request body.
  - `access_token` string, required — The access token associated with the Item for which data is being requested.
  - `account_id` string, required — The Plaid `account_id` of the account that is the funding source for the proposed transaction. The `account_id` is returned in the `/accounts/get` endpoint as well as the [`onSuccess`](https://plaid.com/docs/link/ios/#link-ios-onsuccess-linkSuccess-metadata-accounts-id) callback metadata. This will return an [`INVALID_ACCOUNT_ID`](https://plaid.com/docs/errors/invalid-input/#invalid_account_id) error if the account has been removed at the bank or if the `account_id` is no longer valid.
  - `client_transaction_id` string, required — The unique ID that you would like to use to refer to this evaluation attempt - for example, a payment attempt ID. You will use this later to debug this evaluation, and/or report an ACH return, etc. The max length for this field is 36 characters. The `client_transaction_id` also functions as an idempotency key; calling `/signal/evaluate` with a previously used `client_transaction_id` will return the results of the previous evaluation rather than triggering a fresh evaluation.
  - `amount` number, double, required — The transaction amount, in USD (e.g. `102.05`)
  - `user_present` boolean, nullable — `true` if the end user is present while initiating the ACH transfer and the endpoint is being called; `false` otherwise (for example, when the ACH transfer is scheduled and the end user is not present, or you call this endpoint after the ACH transfer but before submitting the Nacha file for ACH processing). When using a Balance-only ruleset, this field is ignored. This field is not currently used as part of Signal Transaction Score evaluations, but may be used in the future.
  - `client_user_id` string — A unique ID that identifies the end user in your system. This ID is used to correlate requests by a user with multiple Items. Personally identifiable information, such as an email address or phone number, should not be used in the `client_user_id`.
  - `is_recurring` boolean, nullable — Use `true` if the ACH transaction is a part of recurring schedule (for example, a monthly repayment); `false` otherwise. When using a Balance-only ruleset, this field is ignored.
  - `default_payment_method` string, nullable — The default ACH payment method to complete the transaction. When using a Balance-only ruleset, this field is ignored. `SAME_DAY_ACH`: Same Day ACH by Nacha. The debit transaction is processed and settled on the same day. `STANDARD_ACH`: Standard ACH by Nacha. `MULTIPLE_PAYMENT_METHODS`: If there is no default debit rail or there are multiple payment methods. Possible values: `SAME_DAY_ACH`, `STANDARD_ACH`, `MULTIPLE_PAYMENT_METHODS`
  - `user` SignalUser — Details about the end user initiating the transaction (i.e., the account holder). These fields are optional, but strongly recommended to increase the accuracy of results when using Signal Transaction Scores. When using a Balance-only ruleset, if the Signal Addendum has been signed, these fields are ignored; if the Addendum has not been signed, using these fields will result in an error.
    - `name` SignalPersonName, nullable — The user's legal name
      - `prefix` string, nullable — The user's name prefix (e.g. "Mr.")
      - `given_name` string, nullable — The user's given name. If the user has a one-word name, it should be provided in this field.
      - `middle_name` string, nullable — The user's middle name
      - `family_name` string, nullable — The user's family name / surname
      - `suffix` string, nullable — The user's name suffix (e.g. "II")
    - `phone_number` string, nullable — The user's phone number, in E.164 format: +{countrycode}{number}. For example: "+14151234567"
    - `email_address` string, nullable — The user's email address.
    - `address` SignalAddressData, nullable — Data about the components comprising an address.
      - `city` string — The full city name
      - `region` string, nullable — The region or state Example: `"NC"`
      - `street` string — The full street address Example: `"564 Main Street, APT 15"`
      - `postal_code` string, nullable — The postal code
      - `country` string, nullable — The ISO 3166-1 alpha-2 country code
  - `device` SignalDevice — Details about the end user's device. These fields are optional, but strongly recommended to increase the accuracy of results when using Signal Transaction Scores. When using a Balance-only Ruleset, these fields are ignored if the Signal Addendum has been signed; if it has not been signed, using these fields will result in an error.
    - `ip_address` string, nullable — The IP address of the device that initiated the transaction
    - `user_agent` string, nullable — The user agent of the device that initiated the transaction (e.g. "Mozilla/5.0")
  - `risk_profile_key` string, nullable — Specifying `risk_profile_key` is deprecated. Please provide `ruleset` instead.
  - `ruleset_key` string, nullable — The key of the ruleset to use for evaluating this transaction. You can create a ruleset using the Plaid Dashboard, under [Signal->Rules](https://dashboard.plaid.com/signal/risk-profiles). If not provided, for all new customers as of October 15, 2025, the `default` ruleset will be used. For existing Signal Transaction Scores customers as of October 15, 2025, by default, no ruleset will be used if the `ruleset_key` is not provided. For more information, or to opt out of using rulesets, see [Signal Rules](https://plaid.com/docs/signal/signal-rules/).

## Response `200`

OK

- SignalEvaluateResponse — SignalEvaluateResponse defines the response schema for `/signal/evaluate`
  - `request_id` string, required — A unique identifier for the request, which can be used for troubleshooting. This identifier, like all Plaid identifiers, is case sensitive.
  - `scores` SignalScores, nullable, required — Risk scoring details broken down by risk category. When using a Balance-only ruleset, this object will not be returned.
    - `customer_initiated_return_risk` CustomerInitiatedReturnRisk — The object contains a risk score and a risk tier that evaluate the transaction return risk of an unauthorized debit. Common return codes in this category include: "R05", "R07", "R10", "R11", "R29". These returns typically have a return time frame of up to 60 calendar days. During this period, customers of financial institutions can dispute a transaction as unauthorized.
      - `score` integer, required — A score from 1-99 that indicates the transaction return risk: a higher risk score suggests a higher return likelihood.
      - `risk_tier` integer, required — DEPRECATED. Use Signal Rules instead to transform the `score` into a useful action. A tier corresponding to the projected likelihood that the transaction, if initiated, will be subject to a return. In the `customer_initiated_return_risk` object, there are five risk tiers corresponding to the scores: 1: Predicted customer-initiated return incidence rate between 0.00% - 0.02% 2: Predicted customer-initiated return incidence rate between 0.02% - 0.05% 3: Predicted customer-initiated return incidence rate between 0.05% - 0.1% 4: Predicted customer-initiated return incidence rate between 0.1% - 0.5% 5: Predicted customer-initiated return incidence rate greater than 0.5%
    - `bank_initiated_return_risk` BankInitiatedReturnRisk — The object contains a risk score and a risk tier that evaluate the transaction return risk because an account is overdrawn or because an ineligible account is used. Common return codes in this category include: "R01", "R02", "R03", "R04", "R06", "R08", "R09", "R13", "R16", "R17", "R20", "R23". These returns have a turnaround time of 2 banking days.
      - `score` integer, required — A score from 1-99 that indicates the transaction return risk: a higher risk score suggests a higher return likelihood.
      - `risk_tier` integer, required — DEPRECATED. Use Signal Rules instead to transform the `score` into a useful action. In the `bank_initiated_return_risk` object, there are eight risk tiers corresponding to the scores: 1: Predicted bank-initiated return incidence rate between 0.0% - 0.5% 2: Predicted bank-initiated return incidence rate between 0.5% - 1.5% 3: Predicted bank-initiated return incidence rate between 1.5% - 3% 4: Predicted bank-initiated return incidence rate between 3% - 5% 5: Predicted bank-initiated return incidence rate between 5% - 10% 6: Predicted bank-initiated return incidence rate between 10% - 15% 7: Predicted bank-initiated return incidence rate between 15% and 50% 8: Predicted bank-initiated return incidence rate greater than 50%
  - `core_attributes` SignalEvaluateCoreAttributes — The core attributes object contains additional data that can be used to assess the ACH return risk. If using a Balance-only ruleset, only `available_balance` and `current_balance` will be returned as core attributes. If using a Signal Transaction Scores ruleset, over 80 core attributes will be returned. Examples of attributes include: `available_balance` and `current_balance`: The balance in the ACH transaction funding account `days_since_first_plaid_connection`: The number of days since the first time the Item was connected to an application via Plaid `plaid_connections_count_7d`: The number of times the Item has been connected to applications via Plaid over the past 7 days `plaid_connections_count_30d`: The number of times the Item has been connected to applications via Plaid over the past 30 days `total_plaid_connections_count`: The number of times the Item has been connected to applications via Plaid `is_savings_or_money_market_account`: Indicates whether the ACH transaction funding account is a savings/money market account For the full list and detailed documentation of core attributes available, or to request that core attributes not be returned, contact sales or your Plaid account manager.
    - `unauthorized_transactions_count_7d` integer, nullable — We parse and analyze historical transaction metadata to identify the number of possible past returns due to unauthorized transactions over the past 7 days from the account that will be debited.
    - `unauthorized_transactions_count_30d` integer, nullable — We parse and analyze historical transaction metadata to identify the number of possible past returns due to unauthorized transactions over the past 30 days from the account that will be debited.
    - `unauthorized_transactions_count_60d` integer, nullable — We parse and analyze historical transaction metadata to identify the number of possible past returns due to unauthorized transactions over the past 60 days from the account that will be debited.
    - `unauthorized_transactions_count_90d` integer, nullable — We parse and analyze historical transaction metadata to identify the number of possible past returns due to unauthorized transactions over the past 90 days from the account that will be debited.
    - `nsf_overdraft_transactions_count_7d` integer, nullable — We parse and analyze historical transaction metadata to identify the number of possible past returns due to non-sufficient funds/overdrafts over the past 7 days from the account that will be debited.
    - `nsf_overdraft_transactions_count_30d` integer, nullable — We parse and analyze historical transaction metadata to identify the number of possible past returns due to non-sufficient funds/overdrafts over the past 30 days from the account that will be debited.
    - `nsf_overdraft_transactions_count_60d` integer, nullable — We parse and analyze historical transaction metadata to identify the number of possible past returns due to non-sufficient funds/overdrafts over the past 60 days from the account that will be debited.
    - `nsf_overdraft_transactions_count_90d` integer, nullable — We parse and analyze historical transaction metadata to identify the number of possible past returns due to non-sufficient funds/overdrafts over the past 90 days from the account that will be debited.
    - `days_since_first_plaid_connection` integer, nullable — The number of days since the first time the Item was connected to an application via Plaid
    - `plaid_connections_count_7d` integer, nullable — The number of times the Item has been connected to applications via Plaid over the past 7 days
    - `plaid_connections_count_30d` integer, nullable — The number of times the Item has been connected to applications via Plaid over the past 30 days
    - `total_plaid_connections_count` integer, nullable — The total number of times the Item has been connected to applications via Plaid
    - `is_savings_or_money_market_account` boolean, nullable — Indicates if the ACH transaction funding account is a savings/money market account
    - `total_credit_transactions_amount_10d` number, double, nullable — The total credit (inflow) transaction amount over the past 10 days from the account that will be debited
    - `total_debit_transactions_amount_10d` number, double, nullable — The total debit (outflow) transaction amount over the past 10 days from the account that will be debited
    - `p50_credit_transactions_amount_28d` number, double, nullable — The 50th percentile of all credit (inflow) transaction amounts over the past 28 days from the account that will be debited
    - `p50_debit_transactions_amount_28d` number, double, nullable — The 50th percentile of all debit (outflow) transaction amounts over the past 28 days from the account that will be debited
    - `p95_credit_transactions_amount_28d` number, double, nullable — The 95th percentile of all credit (inflow) transaction amounts over the past 28 days from the account that will be debited
    - `p95_debit_transactions_amount_28d` number, double, nullable — The 95th percentile of all debit (outflow) transaction amounts over the past 28 days from the account that will be debited
    - `days_with_negative_balance_count_90d` integer, nullable — The number of days within the past 90 days when the account that will be debited had a negative end-of-day available balance
    - `p90_eod_balance_30d` number, double, nullable — The 90th percentile of the end-of-day available balance over the past 30 days of the account that will be debited
    - `p90_eod_balance_60d` number, double, nullable — The 90th percentile of the end-of-day available balance over the past 60 days of the account that will be debited
    - `p90_eod_balance_90d` number, double, nullable — The 90th percentile of the end-of-day available balance over the past 90 days of the account that will be debited
    - `p10_eod_balance_30d` number, double, nullable — The 10th percentile of the end-of-day available balance over the past 30 days of the account that will be debited
    - `p10_eod_balance_60d` number, double, nullable — The 10th percentile of the end-of-day available balance over the past 60 days of the account that will be debited
    - `p10_eod_balance_90d` number, double, nullable — The 10th percentile of the end-of-day available balance over the past 90 days of the account that will be debited
    - `available_balance` number, double, nullable — Available balance, as of the `balance_last_updated` time. The available balance is the current balance less any outstanding holds or debits that have not yet posted to the account.
    - `current_balance` number, double, nullable — Current balance, as of the `balance_last_updated` time. The current balance is the total amount of funds in the account.
    - `balance_last_updated` string, date-time, nullable — Timestamp in [ISO 8601](https://wikipedia.org/wiki/ISO_8601) format (YYYY-MM-DDTHH:mm:ssZ) indicating the last time that the balance for the given account has been updated.
    - `phone_change_count_28d` integer, nullable — The number of times the account's phone numbers on file have changed over the past 28 days
    - `phone_change_count_90d` integer, nullable — The number of times the account's phone numbers on file have changed over the past 90 days
    - `email_change_count_28d` integer, nullable — The number of times the account's email addresses on file have changed over the past 28 days
    - `email_change_count_90d` integer, nullable — The number of times the account's email addresses on file have changed over the past 90 days
    - `address_change_count_28d` integer, nullable — The number of times the account's addresses on file have changed over the past 28 days
    - `address_change_count_90d` integer, nullable — The number of times the account's addresses on file have changed over the past 90 days
    - `plaid_non_oauth_authentication_attempts_count_3d` integer, nullable — The number of non-OAuth authentication attempts via Plaid for this bank account over the past 3 days
    - `plaid_non_oauth_authentication_attempts_count_7d` integer, nullable — The number of non-OAuth authentication attempts via Plaid for this bank account over the past 7 days
    - `plaid_non_oauth_authentication_attempts_count_30d` integer, nullable — The number of non-OAuth authentication attempts via Plaid for this bank account over the past 30 days
    - `failed_plaid_non_oauth_authentication_attempts_count_3d` integer, nullable — The number of failed non-OAuth authentication attempts via Plaid for this bank account over the past 3 days
    - `failed_plaid_non_oauth_authentication_attempts_count_7d` integer, nullable — The number of failed non-OAuth authentication attempts via Plaid for this bank account over the past 7 days
    - `failed_plaid_non_oauth_authentication_attempts_count_30d` integer, nullable — The number of failed non-OAuth authentication attempts via Plaid for this bank account over the past 30 days
    - `debit_transactions_count_10d` integer, nullable — The total number of debit (outflow) transactions over the past 10 days from the account that will be debited
    - `credit_transactions_count_10d` integer, nullable — The total number of credit (inflow) transactions over the past 10 days from the account that will be debited
    - `debit_transactions_count_30d` integer, nullable — The total number of debit (outflow) transactions over the past 30 days from the account that will be debited
    - `credit_transactions_count_30d` integer, nullable — The total number of credit (inflow) transactions over the past 30 days from the account that will be debited
    - `debit_transactions_count_60d` integer, nullable — The total number of debit (outflow) transactions over the past 60 days from the account that will be debited
    - `credit_transactions_count_60d` integer, nullable — The total number of credit (inflow) transactions over the past 60 days from the account that will be debited
    - `debit_transactions_count_90d` integer, nullable — The total number of debit (outflow) transactions over the past 90 days from the account that will be debited
    - `credit_transactions_count_90d` integer, nullable — The total number of credit (inflow) transactions over the past 90 days from the account that will be debited
    - `total_debit_transactions_amount_30d` number, double, nullable — The total debit (outflow) transaction amount over the past 30 days from the account that will be debited
    - `total_credit_transactions_amount_30d` number, double, nullable — The total credit (inflow) transaction amount over the past 30 days from the account that will be debited
    - `total_debit_transactions_amount_60d` number, double, nullable — The total debit (outflow) transaction amount over the past 60 days from the account that will be debited
    - `total_credit_transactions_amount_60d` number, double, nullable — The total credit (inflow) transaction amount over the past 60 days from the account that will be debited
    - `total_debit_transactions_amount_90d` number, double, nullable — The total debit (outflow) transaction amount over the past 90 days from the account that will be debited
    - `total_credit_transactions_amount_90d` number, double, nullable — The total credit (inflow) transaction amount over the past 90 days from the account that will be debited
    - `p50_eod_balance_30d` number, double, nullable — The 50th percentile of the end-of-day available balance over the past 30 days of the account that will be debited
    - `p50_eod_balance_60d` number, double, nullable — The 50th percentile of the end-of-day available balance over the past 60 days of the account that will be debited
    - `p50_eod_balance_90d` number, double, nullable — The 50th percentile of the end-of-day available balance over the past 90 days of the account that will be debited
    - `p50_eod_balance_31d_to_60d` number, double, nullable — The 50th percentile of the end-of-day available balance between day 31 and day 60 over the past 60 days of the account that will be debited
    - `p50_eod_balance_61d_to_90d` number, double, nullable — The 50th percentile of the end-of-day available balance between day 61 and day 90 over the past 90 days of the account that will be debited
    - `p90_eod_balance_31d_to_60d` number, double, nullable — The 90th percentile of the end-of-day available balance between day 31 and day 60 over the past 60 days of the account that will be debited
    - `p90_eod_balance_61d_to_90d` number, double, nullable — The 90th percentile of the end-of-day available balance between day 61 and day 90 over the past 90 days of the account that will be debited
    - `p10_eod_balance_31d_to_60d` number, double, nullable — The 10th percentile of the end-of-day available balance between day 31 and day 60 over the past 60 days of the account that will be debited
    - `p10_eod_balance_61d_to_90d` number, double, nullable — The 10th percentile of the end-of-day available balance between day 61 and day 90 over the past 90 days of the account that will be debited
    - `transactions_last_updated` string, date-time, nullable — Timestamp in [ISO 8601](https://wikipedia.org/wiki/ISO_8601) format (YYYY-MM-DDTHH:mm:ssZ) indicating the last time that the transactions for the given account have been updated.
    - `is_account_closed` boolean, nullable — Indicates if the account that will be debited is closed
    - `is_account_frozen_or_restricted` boolean, nullable — Indicates if the account that will be debited is either frozen or restricted
    - `distinct_ip_addresses_count_3d` integer, nullable — The number of distinct IP addresses linked to the same bank account during Plaid authentication in the last 3 days
    - `distinct_ip_addresses_count_7d` integer, nullable — The number of distinct IP addresses linked to the same bank account during Plaid authentication in the last 7 days
    - `distinct_ip_addresses_count_30d` integer, nullable — The number of distinct IP addresses linked to the same bank account during Plaid authentication in the last 30 days (max 100)
    - `distinct_ip_addresses_count_90d` integer, nullable — The number of distinct IP addresses linked to the same bank account during Plaid authentication in the last 90 days (max 100)
    - `distinct_user_agents_count_3d` integer, nullable — The number of distinct user agents linked to the same bank account during Plaid authentication in the last 3 days
    - `distinct_user_agents_count_7d` integer, nullable — The number of distinct user agents linked to the same bank account during Plaid authentication in the last 7 days
    - `distinct_user_agents_count_30d` integer, nullable — The number of distinct user agents linked to the same bank account during Plaid authentication in the last 30 days
    - `distinct_user_agents_count_90d` integer, nullable — The number of distinct user agents linked to the same bank account during Plaid authentication in the last 90 days
    - `distinct_ssl_tls_connection_sessions_count_3d` integer, nullable — The number of distinct SSL/TLS connection sessions linked to the same bank account during Plaid authentication in the last 3 days
    - `distinct_ssl_tls_connection_sessions_count_7d` integer, nullable — The number of distinct SSL/TLS connection sessions linked to the same bank account during Plaid authentication in the last 7 days
    - `distinct_ssl_tls_connection_sessions_count_30d` integer, nullable — The number of distinct SSL/TLS connection sessions linked to the same bank account during Plaid authentication in the last 30 days
    - `distinct_ssl_tls_connection_sessions_count_90d` integer, nullable — The number of distinct SSL/TLS connection sessions linked to the same bank account during Plaid authentication in the last 90 days
    - `days_since_account_opening` integer, nullable — The number of days since the bank account was opened, as reported by the financial institution
    - `balance_to_transaction_amount_ratio` number, double, nullable — Taking `available_or_current_balance` and dividing it by the transaction amount. Useful to say "10% buffer", for example. This is a convenience function to build Signal Rules upon.
  - `risk_profile` RiskProfile, nullable — RiskProfile is deprecated, use `ruleset` instead.
    - `key` string — The key of the risk profile used for this transaction.
    - `outcome` string — Legacy method of inspecting the result of the ruleset. New integrations should simply use the "result" property instead. This value will be omitted if you do not have a live existing integration with rules using this field.
  - `ruleset` Ruleset, nullable — Details about the transaction result after evaluation by the requested Ruleset. If a `ruleset_key` is not provided, for customers who began using Signal Transaction Scores before October 15, 2025, by default, this field will be omitted. To learn more, see [Signal Rules](https://plaid.com/docs/signal/signal-rules/).
    - `ruleset_key` string — The key of the Ruleset used for this transaction.
    - `result` 'ACCEPT' | 'REROUTE' | 'REVIEW', required — The result of the rule that was triggered for this transaction. `ACCEPT`: Accept the transaction for processing. `REROUTE`: Reroute the transaction to a different payment method, as this transaction is too risky. `REVIEW`: Review the transaction before proceeding.
    - `triggered_rule_details` RuleDetails, nullable — Rules are run in numerical order. The first rule with a logic match is triggered. These are the details of that rule.
      - `internal_note` string — An optional message attached to the triggered rule, defined within the Dashboard, for your internal use. Useful for debugging, such as "Account appears to be closed."
      - `custom_action_key` string — A string key, defined within the Dashboard, used to trigger programmatic behavior for a certain result. For instance, you could optionally choose to define a "3-day-hold" `custom_action_key` for an ACCEPT result.
    - `outcome` string — The evaluated outcome for this transaction. This field is deprecated, use `result` or `triggered_rule_details.custom_action_key` instead.
  - `warnings` SignalWarning[], required — If bank information was not available to be used in the Signal Transaction Scores model, this array contains warnings describing why bank data is missing. If you want to receive an API error instead of results in the case of missing bank data, file a support ticket or contact your Plaid account manager.
    - `warning_type` string — A broad categorization of the warning. Safe for programmatic use.
    - `warning_code` string — The warning code identifies a specific kind of warning that pertains to the error causing bank data to be missing. Safe for programmatic use. For more details on warning codes, please refer to Plaid standard error codes documentation. If you receive the `ITEM_LOGIN_REQUIRED` warning, we recommend re-authenticating your user by implementing Link's update mode. This will guide your user to fix their credentials, allowing Plaid to start fetching data again for future requests.
    - `warning_message` string — A developer-friendly representation of the warning type. This may change over time and is not safe for programmatic use.

## Other responses

- `default` — Error response.

---

[API](https://skmtc.net/plaid/apis/the-plaid-api.md) · [All operations](https://skmtc.net/plaid/apis/the-plaid-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/plaid/the-plaid-api/versions/64c4514ea59b/schema)
