Underwriting in Under a Second: Real-Time Feature Engineering for Lending

Underwriting in Under a Second: Real-Time Feature Engineering for Lending

Credit decisions used to take days. A borrower submitted a paper application, a loan officer pulled a bureau report, an underwriter reviewed it overnight, and a letter arrived in the mail. Nobody questioned the timeline — it was just how lending worked.

That era is over.

Today, a borrower who applies for a personal loan on a mobile app expects an instant decision. BNPL providers approve or deny in milliseconds at the checkout screen. SMB lenders are underwriting business owners between their lunch break and their next call. Embedded finance products build lending directly into payroll apps, e-commerce platforms, and accounting software — where the borrower never expects to wait at all.

The competitive pressure is one-directional: faster. But faster underwriting is only valuable if the decisions are good. This article walks through how to build a real-time underwriting feature pipeline that can return a credit decision in under a second, without compromising model quality or regulatory standing.

The Evolution of Underwriting: Overnight to Instant

Traditional underwriting was a batch process from end to end. Bureau data was pulled once per application. Income was verified by fax or via a few days of bank statement review. Decisioning happened on a schedule — sometimes daily, sometimes weekly for certain portfolios.

Same-day underwriting emerged as lenders automated bureau pulls and digitized income verification. Human reviewers were replaced by scorecards. Processing time dropped from days to hours. But the underlying architecture was still batch: collect all the data, score it, return a decision.

Instant underwriting changes the architecture, not just the speed requirement. You cannot pull data, process it, and return a result in under a second if any step requires a synchronous query to an external system, a database scan across millions of rows, or any blocking I/O. Every feature the model needs must be precomputed and queryable at low latency at the moment of decision.

That's a feature engineering problem, not a scoring problem.

What Changes with Instant Underwriting

The shift to instant decisioning forces a strict separation between features you can compute in real time and features that are inherently batch.

What you can stream:

  • Bank account transaction events (via open banking APIs like Plaid or MX)
  • Application behavior (time spent on form, revision patterns, device signals)
  • In-session events (how the applicant navigated to the loan, prior visits)
  • Repayment behavior on existing loans (if you hold the serviced portfolio)

What remains batch or near-real-time:

  • Credit bureau data (Experian, Equifax, TransUnion) — bureau pulls are synchronous but expensive; most lenders cache or pre-pull them
  • Income verification via payroll processor integrations (Pinwheel, Argyle) — typically takes seconds to minutes
  • Document-based verification (pay stubs, tax returns) — manual or OCR-assisted, takes minutes to days
  • KYC/AML checks — often synchronous but dependent on third-party SLA

The practical architecture for sub-second underwriting is hybrid: bureau features are computed asynchronously (pre-pull triggered by a soft pull or a prior session touchpoint), while behavioral and cash flow features are maintained as continuously updated materialized views over streaming transaction data.

When the applicant hits "submit," you do not compute anything. You assemble precomputed features from multiple stores, pass them to the model, and return a decision.

Feature Categories for Lending

Bureau Features (Batch, Slow)

Bureau features remain the backbone of credit decisioning. FICO score, revolving utilization, derogatory marks, credit age, and inquiry counts are required inputs for any model that must be defensible under ECOA and FCRA. These features are:

  • Pulled via a hard or soft inquiry at application time
  • Cached for a fixed window (typically 30-90 days, per your credit policy)
  • Joined to the applicant record at model inference time

The latency is manageable if the pull happens before the applicant reaches the decision screen. Pre-pull on form start, cache the result, and serve it instantly at submission.

Bank Connection Features (Near-Real-Time via Open Banking)

When applicants connect their bank account via Plaid or MX, you receive a firehose of historical transaction data — typically 12-24 months of transactions available within seconds of OAuth completion. These transactions are the richest behavioral signal available for thin-file borrowers who lack deep bureau history.

The raw transactions need to be processed before they can be fed to a model. That processing — categorization, income detection, volatility calculation, recurring payment identification — is compute-intensive and should happen asynchronously as soon as the bank connection completes, not at model inference time.

Behavioral Features (Real-Time Streaming)

For borrowers with a prior relationship (existing customers applying for a second loan, or borrowers who have been pre-qualified), you have a continuous stream of behavioral signals: repayment patterns, balance changes, deposit frequency, and spending behavior. These signals update in real time as events arrive.

This is where streaming SQL becomes the right tool. You maintain continuously updated aggregates over these event streams, and your model reads from materialized views rather than recomputing on demand.

Data Architecture: Joining Batch Bureau Data with Real-Time Signals

The architecture has three layers:

Layer 1: Event ingestion. Transaction events, repayment events, and behavioral events stream through Kafka into a streaming database like RisingWave. Events are ingested continuously and aggregated into materialized views.

Layer 2: Feature storage. Precomputed features live in two places: a feature store (Redis or DynamoDB) for low-latency point lookups, and materialized views in the streaming database for features that need to be recomputed continuously. Bureau cache and income verification results live in your application database (Postgres).

Layer 3: Feature assembly and inference. At application submission, a feature assembly service does parallel lookups: bureau features from Postgres cache, behavioral features from the streaming database or feature store, bank connection features from the processed Plaid data. It assembles a feature vector, calls the model endpoint, and returns a decision — all within the SLA window.

Plaid/MX transactions ──► Kafka ──► RisingWave ──► Materialized Views
                                                          │
Bureau pull (async) ──► Postgres cache                    │
                                │                         │
Application submit ──► Feature Assembly Service ◄─────────┘
                                │
                         Model Inference
                                │
                          Decision + Adverse Action

The key insight is that RisingWave maintains the materialized views incrementally — as new events arrive, only the affected aggregates are updated, not recomputed from scratch. This means a feature like "average daily balance over the past 90 days" is always current, and querying it at application time is a simple point lookup with sub-millisecond latency.

SQL Implementation: Behavioral Underwriting Features

Here is the core SQL for the behavioral features that matter most in personal and SMB lending.

Cash flow stability from connected bank account transactions:

-- Cash flow volatility (from connected bank account transactions)
CREATE MATERIALIZED VIEW cash_flow_features AS
SELECT
    applicant_id,
    AVG(daily_balance) AS avg_daily_balance_90d,
    STDDEV(daily_balance) AS balance_volatility_90d,
    MIN(daily_balance) AS min_balance_90d,
    COUNT(*) FILTER (WHERE daily_balance < 0) AS overdraft_days_90d,
    SUM(inflow_amount) AS total_inflows_90d,
    SUM(outflow_amount) AS total_outflows_90d,
    SUM(inflow_amount) - SUM(outflow_amount) AS net_cash_flow_90d
FROM bank_transaction_events
WHERE event_date >= NOW() - INTERVAL '90 days'
GROUP BY applicant_id;

balance_volatility_90d and overdraft_days_90d are among the strongest predictors of default risk in thin-file populations — often more predictive than bureau score alone. A high standard deviation in daily balance signals an applicant living paycheck to paycheck. Overdraft frequency is a proxy for cash flow stress that the bureau misses entirely.

net_cash_flow_90d is a simple DTI signal: if outflows consistently exceed inflows, the borrower has negative free cash flow and any new debt service will worsen their position.

Income stability: recurring income detection:

-- Income stability: recurring income detection
CREATE MATERIALIZED VIEW income_features AS
SELECT
    applicant_id,
    COUNT(DISTINCT EXTRACT(MONTH FROM txn_date)) AS months_with_payroll,
    AVG(amount) FILTER (WHERE is_payroll = true) AS avg_payroll_amount,
    STDDEV(amount) FILTER (WHERE is_payroll = true) AS payroll_variance,
    -- Coefficient of variation: low = stable income
    STDDEV(amount) FILTER (WHERE is_payroll = true) /
        NULLIF(AVG(amount) FILTER (WHERE is_payroll = true), 0) AS income_cv
FROM bank_transaction_events
WHERE txn_date >= NOW() - INTERVAL '6 months'
  AND category IN ('payroll', 'direct_deposit')
GROUP BY applicant_id;

The coefficient of variation (income_cv) is the key feature here. Gig workers and freelancers will have high income_cv; salaried employees will have near-zero. This single feature carries significant information about income predictability, which is the factor that determines whether a borrower can sustain payments during a financial shock.

months_with_payroll catches the risk of recent income disruption — a borrower who had payroll deposits for 4 of the last 6 months may have recently lost their job.

Debt service coverage from existing obligations:

CREATE MATERIALIZED VIEW debt_service_features AS
SELECT
    applicant_id,
    SUM(amount) FILTER (WHERE category = 'loan_payment') AS total_loan_payments_90d,
    SUM(amount) FILTER (WHERE category = 'credit_card_payment') AS total_cc_payments_90d,
    COUNT(DISTINCT merchant_name) FILTER (WHERE category = 'loan_payment') AS distinct_loan_payees,
    -- Estimated monthly debt service
    SUM(amount) FILTER (WHERE category IN ('loan_payment', 'credit_card_payment')) / 3.0
        AS estimated_monthly_debt_service
FROM bank_transaction_events
WHERE event_date >= NOW() - INTERVAL '90 days'
GROUP BY applicant_id;

This gives you an observed DTI numerator, not a self-reported one. Lenders who rely on bureau tradelines for debt service miss obligations that borrowers are paying but are not yet reporting — informal loans, buy-now-pay-later installments, and rent-to-own payments. Bank transaction data catches all of it.

Repayment behavior on existing portfolio (if applicable):

CREATE MATERIALIZED VIEW portfolio_behavior_features AS
SELECT
    borrower_id,
    COUNT(*) FILTER (WHERE days_past_due = 0) AS on_time_payments,
    COUNT(*) FILTER (WHERE days_past_due BETWEEN 1 AND 29) AS late_1_29,
    COUNT(*) FILTER (WHERE days_past_due >= 30) AS late_30_plus,
    MAX(days_past_due) AS max_dpd_lifetime,
    AVG(days_past_due) AS avg_dpd_12m,
    -- Payment rate: how much of the balance they pay each month
    AVG(payment_amount / NULLIF(statement_balance, 0)) AS avg_payment_rate
FROM loan_payment_events
WHERE payment_date >= NOW() - INTERVAL '12 months'
GROUP BY borrower_id;

For existing customers, this is often the strongest predictor available. Behavioral loyalty (consistent on-time payments) is a better leading indicator than bureau score for within-portfolio second loans.

The Decisioning Pipeline: Assembly to Decision in Under 1 Second

The feature assembly step is the integration point. At application submission, the service performs parallel reads from multiple sources:

t=0ms    Application submitted
t=0ms    Parallel lookups begin:
           - Bureau cache (Postgres)         → ~5ms
           - Cash flow features (RisingWave) → ~3ms
           - Income features (RisingWave)    → ~3ms
           - Debt service features (RisingWave) → ~3ms
           - KYC result (Redis)              → ~2ms
t=10ms   All features assembled into vector
t=10ms   Model inference call (hosted endpoint)
t=60ms   Score returned
t=70ms   Policy rules applied (hard cutoffs, state law caps)
t=80ms   Decision + adverse action reason codes generated
t=80ms   Response returned to applicant

The model call is typically the longest step. An XGBoost or LightGBM model served via a low-latency endpoint (SageMaker, Vertex AI, or a self-hosted ONNX runtime) returns in 10-50ms. Neural models take longer; most production lending models use gradient boosting for this reason.

Policy rules run after the model: hard cutoffs for maximum DTI, minimum income, state-mandated APR caps, and any portfolio-level concentration limits. These are deterministic and fast.

The complete pipeline routinely fits within 200ms end-to-end, well within the 1-second budget that modern applicants expect.

Model Governance: Explainability, Adverse Action, and Fair Lending

Speed cannot come at the cost of compliance. Real-time underwriting must satisfy the same regulatory requirements as any other credit decision.

Adverse action notices (ECOA/FCRA). Any denial or less-favorable terms must be accompanied by the principal reasons for the decision — the "adverse action codes." For model-based decisions, this requires SHAP values or another attribution method that maps the decision back to interpretable factors. Your inference pipeline must compute these at decision time and store them durably.

The challenge with streaming features is auditability: the feature values at decision time may differ from what you would compute if you re-ran the query later. You must snapshot the feature vector at the moment of decision and store it alongside the decision record. This is non-negotiable for examination readiness.

Fair lending analysis. ECOA prohibits discrimination on the basis of race, sex, national origin, and other protected characteristics. If your behavioral features (spending patterns, bank account type, transaction categories) are proxies for protected class membership, your model may produce disparate impact even without intent. You need to run regular disparate impact testing against your feature set and model outputs, and document the legitimate business necessity for any features that show correlation with protected characteristics.

Explainability requirements. Many state regulators and the CFPB expect that automated credit decisions can be explained in plain language. "Your cash flow volatility was high relative to your requested loan amount" is an acceptable explanation. "Your embedding vector was outside the decision boundary" is not. Gradient boosting models with SHAP explanations thread this needle — they are performant, auditable, and generate human-readable reason codes.

Model validation. Your streaming features will drift as the economy changes. A model trained on pre-2023 transaction data will have learned income stability patterns that do not reflect current gig economy behavior. You need a monitoring pipeline that tracks feature distribution shift over time, ideally using the same streaming infrastructure you use for feature computation.

FAQ

Can I use bureau data in my real-time model if the pull happens asynchronously?

Yes. The pull does not have to be synchronous with the decision, only the feature lookup. Pre-pull on form start (soft pull), cache the results in your application database, and serve from cache at decision time. The cache TTL should match your credit policy (typically 30-90 days).

What if the applicant doesn't connect their bank account?

Design your model to handle missing features. A separate model or decision tree for applicants without bank connection data is cleaner than imputing missing features. Bureau-only models are well understood and regulatory-defensible; mixed models with imputed behavioral features are harder to explain.

How do I handle the cold start problem for new applicants?

New applicants have no behavioral history in your system. You rely entirely on bureau data and bank connection data. Materialize the bank transaction features as soon as the OAuth connection completes — you typically have 12-24 months of history to compute features from, which is more than enough.

Will regulators accept a model that uses streaming features?

Regulators care about outcomes (disparate impact, adverse action compliance, explainability) more than architecture. A streaming feature pipeline is technically more complex to audit than a batch pipeline, but the same principles apply: snapshot features at decision time, store them durably, generate reason codes, and run regular disparate impact analysis.

What latency should I target for the model endpoint?

For sub-second end-to-end decisions, your model endpoint should return in under 100ms at the 99th percentile. Gradient boosting models (XGBoost, LightGBM) typically return in 5-20ms. Neural models are 50-200ms. If your model is too slow, consider distillation or a simpler model for the first-pass decision, with a more complex model reserved for borderline cases.

How do I prevent feature staleness from affecting decisions?

RisingWave's incremental materialized views update in near-real-time as events arrive — typically within seconds of the source event. For lending decisions, this is more than sufficient. The main risk is ingestion lag from the bank connection provider (Plaid/MX may have delays in transaction posting). Be explicit in your model documentation about the expected data freshness and how stale data affects your feature values.


Instant underwriting is an engineering problem that sits at the intersection of streaming data, feature engineering, and credit risk. The technology to do it well is available today. The regulatory requirements are well understood. What separates teams that succeed from teams that struggle is the architectural clarity to separate precomputation from inference, and the discipline to instrument the pipeline for auditability from day one.

If you are building a lending product and want to discuss how to architect the feature pipeline, the RisingWave community is a good place to start.

Best-in-Class Event Streaming
for Agents, Apps, and Analytics
GitHubXLinkedInSlackYouTube
Sign up for our to stay updated.