Fintech

Writing an Adverse Action Notice When a Model Declines Someone

Antonio Evans9 min read

A compliance analyst at a desk reviewing a printed adverse action notice beside a computer screen showing a model reason code table.
Key points

Key points

  1. 1An adverse action notice must state the specific principal reasons a credit model declined the applicant, in language the applicant can understand, and the legal standard does not change because the model is complex CFPB Circular 2022-03 via the Consumer Finance Monitor.
  2. 2Reason codes should be derived from per-applicant feature attributions (SHAP or LIME) rather than read off a generic variable-importance chart, because the regulation asks what drove this decision, not what matters to the model overall Trussed on specific reasons.
  3. 3A plain-language sentence per code turns a checklist into something an applicant can act on, and Regulation B does not require you to explain how the factor affects creditworthiness CFPB archived innovation blog.
  4. 4The notice should be released only after a human reviewer confirms the decline and the reasons, and every notice should be logged against the model version and inputs that produced it Trussed on ECOA notices.

Before any model code, get the format fixed. Regulation B, at 12 CFR 1002.9, governs the content and timing of adverse action notices, and ECOA at 15 U.S.C. 1691(d) requires a statement of specific reasons when a creditor takes adverse action Trussed on ECOA notices. The CFPB has been explicit that a complex algorithm is not an excuse: the circular responding to "black box" underwriting says creditors cannot justify noncompliance because the technology is too complicated or too opaque the Consumer Finance Monitor analysis of the CFPB circular.

Two practical points follow.

First, the sample reason-code checklists in Appendix C are illustrative, not exhaustive or mandatory. Regulators have stated that creditors must tailor reasons to the factors that actually affected the specific applicant, which means an Appendix C list is a starting vocabulary rather than a ceiling Trussed on specific reasons.

Second, there is useful flexibility. The Official Interpretation to Regulation B says a creditor need not describe how or why a disclosed factor adversely affected the application, and the regulation does not mandate any particular list of reasons, as long as the reasons accurately describe the factors actually considered and scored the CFPB innovation blog. That is what makes plain-language translation feasible without exposing proprietary model internals.

A useful framing comes from fair lending testing practice: score the model output, then the configuration (thresholds, auto-decisioning), then manual review, then the final decision. Disparities can enter at any of those four stages, so the notice has to match the stage that actually caused the decline CrossCheck's fair lending framework.

Generate reason codes from the model, not around it

The core technical problem is that a credit scorecard with a handful of weighted variables has an obvious path from input to decision, while a gradient boosting or neural model produces output from nonlinear interactions across many features with no inherent human-readable path Trussed on ECOA notices.

Legacy explanation methods do not survive that. "Drop one" and impute-median techniques only handle single-direction, single-variable relationships, where an increase in one variable always moves the score the same way; a model with hundreds of interacting credit variables breaks that assumption, which is why Shapley-value attribution from cooperative game theory became the standard approach Zest AI on model explainability.

A decision rule that holds up in practice:

  • If you rely on an industry scorecard, you may rely on the reason codes the consumer reporting agency supplies; you have effectively outsourced the underwriting Zest AI.
  • If you build your own custom model, you must produce your own explanations, and for machine learning that means per-applicant attribution math, not aggregate importance Zest AI.
  • If a vendor supplies the model, you still own the obligation. Lenders retain full compliance responsibility even when the reasons come from a third-party model, so confirm the vendor can produce feature-level explanations adequate for a notice before you go live Trussed on specific reasons.

A minimal attribution pass at decision time looks like this: compute the top contributing features for the declined applicant, rank by absolute contribution in the direction of the decline, and keep the raw attribution values with the decision record.

# Pseudocode: one declined applicant, one model version
attributions = explain(model_version, applicant_features)
negative = sorted(
    [a for a in attributions if a.shap_value < 0],  # pushed toward decline
    key=lambda a: abs(a.shap_value),
    reverse=True,
)
top_features = negative[:4]  # candidate principal reasons

Four is a common working number, not a legal one: Regulation B does not set a fixed count of reason codes per notice, only that the listed reasons reflect the factors that actually affected the applicant Trussed on specific reasons.

The insight most teams miss: attributions are model-level evidence, not applicant-facing text. They need a governed mapping layer that translates feature-level output into standardized reason code categories, and that mapping needs validation against Regulation B commentary so the reasons are principal causes rather than plausible-sounding ones Trussed on ECOA notices.

Map features to a fixed reason code library

Build one governed table that joins three things: the model feature name, an internal reason code, and the customer-facing sentence. Version it alongside the model. When the model changes, the mapping is re-validated, because reasons must remain accurate and stable as the model or its inputs change Trussed on specific reasons.

Feature (internal)Reason codeCustomer-facing sentenceApplicant can act on it?
recent_delinquency_count_12mRC01"Too many recent payments made late or missed."Yes: pay on time, request a fresh report
revolving_utilizationRC02"The balances on your revolving accounts are high relative to your limits."Yes: pay down balances
months_since_last_new_tradeRC04"The length of your credit history is shorter than we require."Partly: time, or an authorized-user arrangement
income_verified_ratioRC07"We could not verify enough of the income you listed."Yes: supply documents
cashflow_volatility_indexRC11"Your recent account activity shows large swings in deposits."Yes: explain seasonality, provide statements
model_version_hash / threshold_band(never disclosed)(never disclosed)No

The last row matters. "You failed to achieve a qualifying score" is explicitly insufficient under Regulation B, and a reason code that restates the threshold teaches the applicant nothing CrossCheck on adverse action denial reasons. Keep infrastructure and threshold metadata out of the disclosure and in the audit file.

Two quality gates on this table:

  1. Specificity. Does the sentence name a factor the model actually used for this applicant? A loosely matched code is not a compliance strategy if it is not the real reason CrossCheck.
  2. Actionability. Can a reasonable applicant change the outcome next time, or request a correction? If not, the sentence is decoration.

Write the sentence a customer can use

Plain language here is a narrow craft, not general readability. Three rules do most of the work.

Name the factor, not the score. "Insufficient income" on its own is weak; "we could not verify enough of the income you listed" tells the applicant what to fix. Regulation B requires the specific principal reasons, and generic statements about internal standards or policy are not substitutes CrossCheck.

Do not try to teach the model. The Official Interpretation allows you to disclose a factor even when its relationship to creditworthiness is not intuitive to the applicant, so you are not obliged to explain the mechanics of a cash-flow volatility feature CFPB innovation blog. This is the single biggest time saver in drafting.

Keep the FCRA layer separate. When the action is based in whole or in part on a credit score from a consumer reporting agency, the notice must also disclose the key factors that adversely affected the score, the agency's name and contact information, and additional content CFPB innovation blog. Build the notice as two blocks: FCRA disclosures, then your own model reasons. Do not blend them, and do not let a templating engine drop one block.

This is also where customer-service quality becomes a compliance asset. In Deloitte's survey of US banking customers and executives, 71 percent ranked ease of resolving issues as their top support factor, 28 percent said repeated negative contact-center experiences made them reduce spending with the bank, and 31 percent stopped doing business with the institution altogether Deloitte Insights. A decline notice is the first contact-center interaction after a rejection. If the reasons are vague, the call becomes a dead end, and the customer's next action is often to leave.

If your team is drafting and testing this language at volume, a Quanta Expert can take the reason-code library and your tone guide and return finished sentence sets for review: https://quantaintelligence.ai/agents

Put a human in front of the release

The notice should not leave the building until a person has confirmed two things: that the decline decision is correct, and that the listed reasons match what the model did for that applicant.

Build the review as a queue, not a courtesy. Each item carries the application file, the model score band, the top attributions, the proposed reason codes and the drafted sentences. The reviewer's job is to check fit, not to re-underwrite from scratch, so the queue needs a time standard that matches the decision timeline your jurisdiction imposes.

Review stepQuestion the reviewer answersPass criteriaCommon failure
Decision checkIs the decline consistent with policy and the applicant file?No override pending; documents match inputsInputs entered wrong upstream
Reason checkDo the codes match the top attributions?Same feature set, same directionStale mapping after a model update
Language checkWould a customer understand and could they act?No jargon, no score restatementJargon smuggled in from feature names
Disclosure checkAre FCRA elements present and correct?Score factors, agency name, contactTemplate dropped the block
Record checkIs the notice tied to model version and inputs?Decision ID, version hash, timestampsReasons stored without version

Two things justify the human step beyond good service. First, overrides and manual review are their own stage in the credit funnel, and disparities can originate there rather than in the model, which means a reviewer who rubber-stamps notices removes your ability to detect that CrossCheck's funnel analysis. Second, supervisory thinking has moved decisively toward keeping a human in control of critical automated decisions while requiring auditable governance even where the underlying model is not fully transparent Moody's on AI model risk standards.

Set a decision rule for disagreements. If the reviewer's file review contradicts the model, the decline goes back to underwriting with a written note, and no notice is sent. If the reviewer agrees with the decline but not the reasons, the mapping is corrected and the notice regenerated from the corrected library, with both versions retained. Never edit reasons by hand in the notice itself.

Keep the record so the notice matches what the model did

The notice is evidence. A regulator or auditor reconstructing a decline needs to see the model version, the inputs, the attributions, the reason mapping in force that day, the reviewer identity, and the exact text sent. If any link is missing, you cannot demonstrate that the disclosed reasons were accurate, and accounting firms describe exactly this documentation burden for AI and third-party models, including the expectation that a knowledgeable third party could recreate the model's behavior from the record Kaufman Rossin.

A workable record schema:

  • decision_id, application_id, timestamp of model score
  • model_id and version hash
  • input vector snapshot (or a hash plus a retrievable store)
  • raw attribution values for the top contributing features
  • reason code library version and mapped codes
  • drafted notice text hash, reviewer ID, review timestamp and outcome
  • send timestamp and channel

Run a monthly reconciliation: sample notices, replay them against the stored inputs and the stored library version, and confirm the notice text reproduces. Drift shows up here first. Supervisory frameworks increasingly expect model risk management to catch performance degradation and unreliable behavior over time, and reason codes that stop matching the model are a leading symptom Moody's. Note also that the OCC's updated model risk management guidance is risk-based and tailored to institution size and model use, and that generative and agentic AI models sit outside its scope, which means your own documentation discipline, not a checklist, is what carries the file OCC News Release 2026-29.

If translation to plain language is generated by a model in your pipeline, route it through a single API so the prompt, model version and output are all captured in one place: https://quantaintelligence.ai/inference

A worked example

An applicant is declined by a gradient-boosted model. Raw attributions show four features pushing toward decline: recent delinquency count, revolving utilization, months since last new trade, and a cash-flow volatility index. Two more features pushed slightly toward approval but were outweighed.

Wrong notice: "Your application did not meet our credit criteria. You failed to achieve a qualifying score." This is insufficient, because it restates the creditor's internal standard rather than the specific reasons CrossCheck.

Also wrong: "Our AI model found your application too risky." Model complexity is not a defense, and "the AI decided" is not a reason Consumer Finance Monitor on the CFPB circular.

Right notice, four reasons, each tied to a factor the model actually used: too many recent late or missed payments; high revolving balances relative to limits; a credit history shorter than required; large recent swings in deposits. Plus the FCRA block, because a credit score was part of the file CFPB innovation blog.

Then the reviewer confirms the file, the codes match the attributions, the sentences pass the language check, and the notice is released with a record of all four steps. The applicant calls, hears the same four reasons, and knows which two are fixable within a quarter.

FAQ

FAQ

Is it acceptable to tell the applicant only that they failed to meet the cutoff?

No. Regulation B states that a statement that the applicant failed to achieve a qualifying score on the creditor's credit scoring system is insufficient, and the notice must give the specific principal reasons for the action CrossCheck's summary of Regulation B. Generic statements about internal standards or policy fail for the same reason.

Can we use a vendor model and rely on the vendor's reasons?

You can use the vendor's explanations, but the compliance obligation stays with you, and choosing a reason that is close but inaccurate is not a defense Trussed on specific reasons. Before deployment, confirm the vendor produces feature-level, per-applicant explanations adequate for a notice, and validate the mapping yourself.

Does Regulation B specify how many reasons we must list?

No. There is no fixed count; the requirement is that the reasons reflect the factors that actually adversely affected that applicant, so practices vary by lender Trussed on specific reasons. Most teams settle on a short list chosen for specificity and actionability rather than extending the list to fill space.

Do we have to explain how each factor affects creditworthiness?

Not under Regulation B's Official Interpretation. A creditor need not describe how or why a disclosed factor adversely affected the application, and no particular list of reasons is mandated as long as the disclosed factors were actually considered and scored CFPB innovation blog. You still have to name the right factors accurately.

What should we do when using a model makes the reasons harder to produce?

Treat it as a deployment readiness question rather than a legal one. Complex models can rely on nonlinear relationships that do not translate neatly into consumer language, and if the institution cannot reliably identify and communicate the principal reasons, the model may not be ready for regulated decisioning CrossCheck on adverse action denial reasons. Either build the attribution and mapping layer, or hold the model.

Sources

Sources

  1. ECOA Adverse Action Notices for AI Credit Decisions
  2. on Machine Learning Model Adverse Action Notices
  3. Specific Adverse Action Reasons for AI Lending Models
  4. Providing adverse action notices when using AI/ML models
  5. Fair Lending Risk in AI Credit Models: A Compliance Framework for Lenders
  6. Getting Model Explainability Right Is Everything In AI Lending - Zest AI
  7. Adverse Action Denial Reasons in the Age of AI
  8. AI-assisted customer service in banks | Deloitte Insights
  9. Regulators drive new standards for AI model risk management - Moody's
  10. Managing AI model risk in financial institutions: Best practices for ...
  11. OCC Issues Updated Model Risk Management Guidance
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles