Lincoln Financial
Try the planner

codeLinc 11 · Lincoln Financial

Steadfast: a life insurance needs plan, talked through

A conversation that walks someone through their family, income, debts and coverage, then shows how much life insurance would look after them, with every number explained and nothing meant to worry them. Priced by an underwriting model we trained ourselves.

For the judges

Where to find each criterion

The ten criteria from the judging page, in order, and where each one is answered here.

  1. 1User interface & intuitivenessOne question at a time, chips for every answer, 16px type, and a plain explanation beside every insurance word.
  2. 2Functional requirements & impactDependents, income, debts and coverage go in. A priced needs plan with its math comes out, plus term vs permanent life.
  3. 3Solution design & innovationWe trained our own underwriting model, so every price on the page comes from this person's answers.
  4. 4Demonstration and presentationReal screenshots of every screen on this page, and a timed four-minute runbook.
  5. 5Does it work?The core flow runs signed out, end to end, with 450 automated tests behind it. Lincoln's own policies and advisors are built into the result.
  6. 6Technology platforms employedNext.js 16, PyTorch, FastAPI, Neon Postgres, the OpenAI Responses API, Vercel, and open data from the CDC and the Society of Actuaries.
  7. 7Security accommodationsDesigned around US insurance rules first, then hashed tokens, zod on every boundary, and a model that sees only a fact sheet.
  8. 8Technical creativityThe model explains and the code calculates. Any figure the model invents is caught before it reaches the screen.
  9. 9Architecture & methodologyA user flow through the model and the underwriting engine, a storyboarded conversation, docs as contracts, and a timestamped log of the night.
  10. 10ComplexityAn underwriting engine we trained ourselves: a survival model fitted to 47,469 US adults and priced with Society of Actuaries tables, held to 0.99 observed-to-expected deaths. Plus one API route, no chart library, and 450 tests that run in three seconds.

The prompt

What we were asked to build

“A conversational AI tool that walks someone through their situation (dependents, income, debts, existing coverage) and produces a personalized life-insurance needs assessment, explaining the reasoning and the math clearly without causing anxiety.”

Stretch goal: explain term vs whole life and surface the tradeoffs that apply to this person.

Most people who need this have never bought life insurance, and many of them are older. So Steadfast asks one question at a time, says why it asks, and accepts answers the way people actually say them.

The result leads with one rounded figure and a reasonable range, never an exact verdict, and every dollar traces to a line in the ledger. Term vs permanent life is shown in their own numbers, with the closer fit marked and the reason. Both stretch tradeoffs, buying term and putting the difference aside and splitting into two policies, come from the engine.

Core features

What it does, in eight pieces

Each part of the prompt, working in the app today. The tour that follows shows every screen.

Feature tour

Every screen

Captured from the running app as the demo family. Open any one for the full-size screenshot.

  1. steadfast/calculator
    A conversation, one question at a time

    01

    A conversation, one question at a time

    Family, income, debts, then coverage. Every question has answer chips, a line on why we ask, and room to type in their own words: “240k”, “$1,800 a month”, “my wife smokes, I don’t”. It works signed out.

  2. steadfast/calculator
    The plan builds while they talk

    02

    The plan builds while they talk

    One rounded figure with a reasonable range, and a ledger beside it. Every row states its assumption, like “10 years × $85,000”, and the rows always add up to the figure.

  3. steadfast/calculator
    Five charts, drawn by hand

    03

    Five charts, drawn by hand

    How the number adds up, the family timeline, income continuity, a cost explorer and term vs permanent life. Plain SVG with no chart library, so each one fits a 390px phone.

  4. steadfast/calculator
    A full plan and an essentials plan

    04

    A full plan and an essentials plan

    Plan A covers everything they told us about. Plan B keeps the essentials at a lower monthly cost. Both are priced side by side, each names the Lincoln policy it’s closest to, and they choose.

  5. steadfast/calculator
    Term or permanent life, in their dollars

    05

    Term or permanent life, in their dollars

    Two sentences of explanation, then only the rows that differ for them, with the closer fit marked and the reason. It also shows buying term and putting the difference aside, and a two-policy option when that costs less.

  6. steadfast/calculator
    Prices from our own underwriting model

    06

    Prices from our own underwriting model

    Two short health questions, about tobacco and four common conditions, set a health class with a plausible range. Build is assumed average, and the page says so. The class, the range and the prices come from the model we trained.

    How the model was trained
  7. steadfast/calculator
    Questions answered from their own plan

    07

    Questions answered from their own plan

    After the plan, they can ask anything: “Why until my youngest is 22?” Answers use only the figures on their page.

  8. steadfast/dashboard
    Save it, and come back to a dashboard

    08

    Save it, and come back to a dashboard

    Saving needs only an email; a link to choose a password follows. The dashboard holds the plan, one-tap life changes, health toggles and a chat that reprices everything live.

    See the dashboard
  9. steadfast/calculator
    Something to take to the kitchen table

    09

    Something to take to the kitchen table

    A one-page PDF of the assessment, with the math, term vs permanent life and the pricing notes, and a request for a licensed Lincoln Financial advisor to call, with their plan attached.

  10. steadfast/calculator
    Written for people buying for the first time

    10

    Written for people buying for the first time

    Insurance words come with an info button and a plain explanation that opens on tap, hover or keyboard. A plain-words guide answers the questions people ask before they buy, from the same glossary, and a test keeps every explanation to three sentences with no jargon. Reading text is 16px or larger, and there’s no red anywhere.

    Read the guide
  11. steadfast/calculator
    Just as calm on a phone, screen 1 of 2Just as calm on a phone, screen 2 of 2

    11

    Just as calm on a phone

    Below tablet width the conversation and the plan become two tabs, so each gets the whole screen. Chips stay large enough to tap, and every chart redraws to fit.

The dashboard

A plan that keeps up with their life

A saved plan comes back here. Every control on the page changes the same facts the conversation collected, so the whole plan, its prices and the chat move together.

  1. steadfast/dashboard
    The saved plan, as it opens

    01

    The saved plan, as it opens

    The figure, its range and the ledger, with the planner’s welcome in the chat beside it. What-if controls sit right under the number: years of support, college, the mortgage.

  2. steadfast/dashboard
    Life Canvas: one-tap life changes

    02

    Life Canvas: one-tap life changes

    The Life Canvas lets them sketch a different version of their life and watch the plan redraw. “A new baby”, “A 20% raise”, “Paid off the mortgage”, “Partner stops working”, “Five years from now”: each one is a patch of facts over the saved answers, so the whole plan recomputes and the chat says what moved. Nothing is saved until they say so.

  3. steadfast/dashboard
    Health changes reprice through the model

    03

    Health changes reprice through the model

    Toggling “High blood pressure” sends the new answers to the underwriting engine. Plan A moves from $162 to $210 a month, the class is named, and every chart redraws. “Make this my plan” keeps it.

The chat on the right is the same planner, smaller and collapsible. Typing “we rent now” or “I’m 36 now” applies the change the same way a chip does, and suggested questions like “Why until my youngest is 22?” are answered from the plan’s own fact sheet.

Nothing is saved until “Make this my plan”. The saved plan is one row per account, so saving again replaces it, and “Talk to an advisor” sends the current plan with the request.

User flow

What happens at each step

The person's path from the first question to a saved plan, with what runs behind each step: the app does the math, the model reads and explains, and our underwriting engine sets the class and the price. Every remote piece has a local fallback, so the demo never stalls.

  • The app

    Next.js, plain TypeScript. Owns the conversation and every dollar of math.

  • The model

    OpenAI Responses API. Reads and explains, never calculates.

  • Underwriting engine

    Our survival model and SOA pricing, as a Python service.

  1. Step 1

    They start a conversation

    From the landing page into the planner, signed out. Four stages, one question at a time: family, income, debts, coverage.

    Behind it

    • The app

      Picks the next question from the step list, with answer chips and a line on why we ask.

    If it fails: Nothing remote yet. The first screen never waits on a network call.

  2. Step 2

    They answer in their own words

    A chip, or a typed sentence like “married, two kids, 4 and 9, I make about 90k”.

    Behind it

    • The app

      A local parser reads money shorthand, ages, households and health first.

    • The model

      Messages of 8 words or more also go to the model, which returns structured facts the app checks field by field. Answers are cached by request hash.

    If it fails: No key or a slow reply: the local parse stands and the conversation moves on.

  3. Step 3

    The plan builds beside them

    Each answer adds a line to the ledger on the right: income to replace, the mortgage, college, minus what work already covers. The total updates as they go.

    Behind it

    • The app

      Needs math in ordinary code: the ledger, the range, and Plan A against Plan B. The model never touches a number here.

    If it fails: Pure arithmetic in the browser. There is nothing to fall back from.

  4. Step 4

    Two health questions

    Tobacco, and whether a doctor has ever told them about blood pressure, diabetes, heart disease or cancer. Build is assumed average and the page says so.

    Behind it

    • Underwriting engine

      Age, sex and the health answers go to the engine with the needs. It returns an underwriting class with its plausible range, term prices from 10 to 30 years, a permanent life price with guaranteed cash values, a recommended term, and sometimes a cheaper two-policy ladder.

    • The app

      Requests are debounced 300 ms, checked with zod and cached. Both plans are priced and the charts refit to the engine's curve.

    If it fails: Engine down: local rate tables price the plan silently, and the page still reads as finished.

  5. Step 5

    The plan is explained

    A summary streams into the chat: what the money does, in their words, with the range and the monthly price for term and permanent life side by side.

    Behind it

    • The app

      Builds a fact sheet from the plan and the engine's prices: every figure the model is allowed to say, already formatted.

    • The model

      Writes the summary from the fact sheet only, three sentences a bubble.

    • The app

      A number guard checks the text. Any dollar figure not on the fact sheet is logged and that answer is never cached.

    If it fails: No model: a template summary from the same fact sheet takes its place.

  6. Step 6

    They ask what if

    “What if I paid off the mortgage?”, “Why 20 years?”, or a preset like “A new baby”. The plan and the prices move with the answer.

    Behind it

    • The app

      Known what-if commands run instantly as plan overrides, with no round trip.

    • The model

      Open questions get an answer from the fact sheet plus structured overrides the app applies by name, never free text.

    • Underwriting engine

      Every change reprices through the engine, so the chat and the page never disagree.

    If it fails: Canned answers cover the common questions and the overrides still apply.

  7. Step 7

    They keep it

    Download the PDF, ask for an advisor, or save the plan to an account and come back to the dashboard.

    Behind it

    • The app

      Plan saved to Postgres, one per account. A set-password link goes out by email, stored only as a hash and good for 24 hours.

    If it fails: No email key: the link prints to the server log and the demo continues.

How it works

From an answer to an explained plan

The model reads and explains. Arithmetic is ordinary code, so a figure on screen is never a guess.

  1. Step 1

    They answer

    A chip or a typed sentence.

  2. Step 2

    Read locally

    A parser handles money shorthand, ages, households and health. Messages of 8 words or more, or ones it can’t read, also go to the model.

  3. Step 3

    Needs math

    Plain TypeScript works out the plan and its ledger. The model never computes money.

  4. Step 4

    Priced

    Our underwriting engine prices both plans. Requests are debounced and cached.

  5. Step 5

    Fact sheet

    Every figure the model may say, already formatted, built from the plan and the engine’s prices.

  6. Step 6

    Explained

    The model writes the summary and answers from the fact sheet only.

  7. Step 7

    Checked

    Any dollar figure that isn’t on the fact sheet is logged, and that answer is never cached.

The underwriting engine

We trained our own underwriting model

No public API turns health answers into an underwriting class and a price. So we built one from public data: a PyTorch survival model trained on 47,469 US adults, priced with Society of Actuaries tables, and checked against published rates.

PyTorch

Trained with PyTorch, served with numpy

A Gompertz proportional-hazards survival network, trained five times on bootstrap resamples with the exact left-truncated, right-censored likelihood. The trained ensemble is exported to 112 KB of numpy weights, so the live engine serves without PyTorch and fits a Vercel Python function. One script rebuilds everything from the public sources in about 30 seconds after the download.

  1. Step 1

    The cohort

    47,469 US adults from the CDC’s NHANES surveys, 1999 to 2018, linked to the National Death Index: 5,448 deaths over 467,880 person-years. Split 70/15/15, and the test set was never touched in training.

  2. Step 2

    The model

    A proportional-hazards survival model on the attained-age scale with a Gompertz baseline per sex and a two-layer network (64 units, SiLU) over ten answers. The exact left-truncated, right-censored likelihood, weighted by survey weights, in PyTorch.

  3. Step 3

    The range

    Five models trained on bootstrap resamples. Their spread is the best-to-worst plausible class, which is where the “ranges, not verdicts” rule gets its numbers.

  4. Step 4

    The classes and prices

    Relative risk against an insured-like pool sets Preferred Plus through Table 8 at the industry class mix, with carrier-style knock-out rules. Society of Actuaries 2015 VBT mortality and an actuarial premium formula set the price, calibrated to 152 published rates with 115 held out.

  • 47,469

    US adults in the training cohort (NHANES 1999–2018, linked to the National Death Index)

  • 0.837

    10-year C-index on held-out people (age and sex alone: 0.792; a Cox model: 0.832)

  • 0.99

    Observed over expected deaths on the test set

  • 26 / 26

    Pricing and underwriting invariants pass, like “longer terms never cost less”

Observed deaths against expected deaths by risk decile on the held-out test set, lying along the diagonal
Does the risk model tell the truth? Held-out people sorted into ten risk groups. Expected deaths against observed deaths fall on the line, from the healthiest decile to the riskiest.
Engine monthly premiums against published monthly premiums for term and permanent life, with a ±25% band
Do the prices match the market? Engine premiums against 267 published prices, 115 of them held out of calibration. Term lands within 8.2% on average; published charts disagree with each other by a median of 28% for the same policy, and the engine sits inside their spread 86% of the time.

The learned risk ratios were checked against published epidemiology: current smoking at 2.9×, former smoking, heart disease, hypertension and cancer all land in the published range. Diabetes, poor self-rated health and low weight run higher than the literature, severe obesity lower, and the report says so; underwriting rules like the build chart and the diabetes cap cover those gaps.

For each request the engine returns a class with a best-to-worst range, prices for 10 to 30-year terms, permanent life with guaranteed cash values, a recommended term tied to when each need ends, and a term-vs-permanent comparison in this person’s dollars. Every number in the validation report is regenerated by one script, and a model-card endpoint publishes the metrics. These are planning estimates, not quotes.

Compliance

Built inside the rules for talking about life insurance

Life insurance is regulated by the states under the McCarran-Ferguson Act, through model laws from the National Association of Insurance Commissioners that most states adopt. A tool that discusses coverage and prices is doing something those laws govern, even when it sells nothing. We read them before the first screen, and they set the limits on what Steadfast says.

  • Estimates, never quotes

    NAIC Unfair Trade Practices Act (Model 880) · Advertisements of Life Insurance Model Regulation (Model 570)

    Misrepresenting a policy’s terms, benefits or price is an unfair trade practice in every state. So every price on every screen says “planning estimates, not quotes”, the health class says the insurer decides after its review, and the PDF says it is not a quote, application or underwriting commitment. The model is instructed never to say someone qualifies or to quote a Lincoln price.

  • Guaranteed and not-guaranteed kept apart

    NAIC Life Insurance Illustrations Model Regulation (Model 582) · Standard Nonforfeiture Law (Model 808)

    An illustration may not show a non-guaranteed value as if it were guaranteed. Permanent life cash values here are the guaranteed minimums under the Standard Nonforfeiture Law on the 2017 CSO table, labeled “guaranteed minimum”. The “put the difference aside” figure at 5% sits next to it labeled “not guaranteed”, and the chart says which is which.

  • A licensed advisor makes the recommendation

    NAIC Producer Licensing Model Act (Model 218)

    Only a licensed producer may sell, solicit or negotiate insurance. Steadfast shows the tradeoffs in the person’s own numbers and explains why one fits closer; the application and any recommendation come from a licensed Lincoln Financial advisor, who gets the worked plan with the request. The two kinds are named the way Lincoln names them, term life and permanent life, and Lincoln’s permanent product is named truthfully as universal life, not whole life.

  • Underwriting factors a regulator would recognize

    State unfair-discrimination statutes (Model 880, §4) · Colorado SB 21-169 and Regulation 10-1-1 · NAIC Model Bulletin on the Use of AI Systems by Insurers (2023)

    Life insurers may classify only by factors tied to expectation of life, and newer rules ask that algorithms be governed, tested and explainable. The engine uses age, sex, build, tobacco, four doctor-diagnosed conditions and self-rated health: the questions on a paper application. No credit score, ZIP code, occupation or purchased consumer data. Every quote returns the factors that moved the class with their multipliers, a model-card endpoint publishes the metrics, and the validation report is regenerated by a script.

  • Financial and health information, kept to a minimum

    Gramm-Leach-Bliley Act, Title V · NAIC Privacy of Consumer Financial and Health Information Regulation (Model 672) · Insurance Data Security Model Law (Model 668) · NYDFS Part 500

    Health answers collected for insurance are protected information. The planner works without an account, so most people never store anything. A saved plan holds the five health answers and the facts behind the ledger, and nothing else. The model never receives a name, email or account, and its storage is off. There are no third-party analytics or advertising pixels, and every email is one the person asked for.

  • Plain language and reachable controls

    NAIC Policy Language Simplification Model Act (Model 575) · ADA Title III and WCAG 2.1 AA

    States require policies to pass a reading-ease test, and courts apply the ADA to websites. The chat is held to three sentences a turn, every insurance word has a glossary entry that a test keeps jargon-free, reading text is 16px or larger, every control works by keyboard with a visible focus ring, nothing is conveyed by color alone, and motion respects the reduced-motion setting.

Security

What we protect, and how

People tell a planner about their family, money and health. We keep as little as we can, and the model sees less than that.

  • Only hashes of tokens are stored

    Session cookies and password links are 32 random bytes. The database keeps only their SHA-256, so a copy of the tables can’t be used to sign in.

  • Passwords: bcrypt, cost 12

    At least 8 characters and at most 72 bytes, since bcrypt ignores anything longer. A wrong email is checked against a dummy hash, so it takes as long as a wrong password.

  • Every input is checked on the server

    Each server action and the one API route validate their input with zod. Amounts are worked out again on the server, never trusted from the browser. Model and engine replies are parsed against a schema too.

  • Links are single-use and short-lived

    A password link lasts 24 hours and is claimed in one transaction, so it can’t be used twice. Setting a password signs out every other session. One link a minute per account, and the same reply whether or not an email has an account.

  • No personal data in URLs

    Plans live in the database, tied to the account, never in the address bar. The only query value anywhere is a random password-link token. Cookies are httpOnly, SameSite=Lax, and secure in production.

  • The model sees only the fact sheet

    It never gets a name, email or account. It receives their answer or question, the plan’s fact sheet and the chat so far, with storage turned off (store: false). Its output is structured, and the app applies only known fields.

Does it work?

Would Lincoln Financial use it?

The judging note says the core functionality counts for far more than login and password reset. That is how it was built.

  • The core flow needs no account

    Anyone can open the planner and get a priced, explained plan in a few minutes. Sign-in exists only to come back later; it is never on the path to the result.

  • Lincoln’s products are in the answer

    Each plan names the Lincoln policy it’s closest to: TermAccel under $1M with the quick application, LifeElements for larger amounts or older ages, and WealthPreserve 2 IUL for permanent coverage. Terms snap to Lincoln’s 10, 15, 20 or 30 years, and the age and amount limits are checked.

  • It hands a qualified lead to an advisor

    “Talk to an advisor” records a request with the person’s email, a preferred time and the plan attached, so the advisor opens the call with a worked needs analysis instead of a blank form.

  • What it would take to ship

    Swap the engine’s calibrated public rate charts for Lincoln’s rate book and underwriting classes, add filed disclosures, and keep everything else. The engine is one service behind a typed contract, so that swap touches nothing in the app.

Tested

450 automated tests

Everything the plan says about money, every input boundary and every account action is pinned by a test. The suite runs in under three seconds.

  • 450

    Automated tests

    16 files, under three seconds, run with the typecheck and lint before any change lands.

  • 351

    Unit

    The pure library: the answer parser, the needs math, the conversation order, every schema, pricing, the Lincoln policy match, the what-if answers, and every glossary explanation.

  • 89

    Integration

    Server actions and page readers with the model, the engine and the database mocked: accounts and password links, the model’s three tasks, the engine’s tradeoffs, and what each page shows a visitor.

  • 10

    Design rules

    A lint over the source for the design system: no hex colors, no font sizes off the scale, no round corners, no removed focus rings, no emoji.

Some of what they pin down

  • The ledger rows always add up to the figure, and no row is negative.
  • The range brackets the estimate and never offers nothing as reasonable.
  • Plan B never costs more than Plan A.
  • Step order follows Family → Income → Debts → Coverage, and never goes back.
  • Only token hashes are stored; a password link works once; a reset signs out every other session.
  • Every glossary explanation is at most three sentences, with no jargon inside it.
  • “1.2 million”, “$1,800 a month” and “my wife smokes, I don’t” are read the way a person meant them.
  • The term-vs-permanent chart and “put the difference aside” agree to the dollar.

The underwriting engine has its own checks: 26 pricing and underwriting invariants, like “a longer term never costs less” and “adding a condition never improves the class”, rerun by the validation script with the held-out metrics.

End to end, a scripted browser walkthrough drives the real app as the demo family before each demo and captures the screenshots on this page, at desktop width and at 390px.

Methodology

How we worked

Storyboard, design system and docs first, then code against them, then verify the same way every time.

  1. 01

    The conversation was storyboarded first

    Family → Income → Debts → Coverage was fixed before any code, and every question was written with its chips, its “why we ask” and the fact it sets. The plan panel follows the same order, so the right side answers the left side.

  2. 02

    A design system before any screen

    Tokens, nine components and the content rules (three sentences a turn, ranges not verdicts, no red, no exclamation marks about money) were written into docs/design and ported into typed components. A design lint test fails the suite when a screen breaks them.

  3. 03

    Docs as contracts

    One project brief for every contributor, human or AI, with the architecture, the rules and the roadmap. A presenter’s runbook with a timed script. A night log with one entry per change: what, why, files, how it was verified.

  4. 04

    A roadmap, worked in order

    Saturday 13:34 first commit, 14:55 design system, 15:07 planner, 18:52 landing, 23:16 dashboard, 00:53 engine deployed as a Vercel service. Then the agreed list: the engine’s tradeoffs in the UI, demo hardening with a seeded account and pre-warmed answers, deploy.

  5. 05

    Verified the same way every time

    Typecheck, lint, build, 450 tests, then driving the real app in a browser at desktop width and at 390px. The screenshots on this page are captured by a script from the running app, as the demo family.

pnpm demo starts the app and the engine together, and pnpm demo:seed resets the demo account and caches the model’s answers for the demo family ahead of time, so the summary and the suggested questions answer instantly.

Complexity

Only as complex as it has to be

Each piece earns its place by doing something the others can’t. Where a dependency would only add weight, there isn’t one.

What we built, and why

  • A Python service, because turning health answers into a mortality risk and an actuarial price needs survival models and SOA tables that don’t belong in a browser.
  • A fact sheet and a number guard between the model and the screen, because a language model near money needs a fence.
  • A local parser beside the model, because a short answer like “85k” shouldn’t wait on the network.
  • A term-vs-permanent comparison with “put the difference aside” and a two-policy option, because the stretch goal asked for tradeoffs that apply to this person.

What we left out, on purpose

  • No chart library. Five charts are plain SVG, so they fit a phone and take no dependency.
  • One API route (the streamed summary). Everything else is a server action with a zod schema.
  • No state or form library. React 19 and the derived-state pattern were enough.
  • No auth framework. Accounts are an email, a bcrypt hash and a hashed session token over six Postgres tables, kept off the critical path.
  • Serving with numpy only: the trained ensemble is 112 KB of weights, so the engine fits a Vercel Python function. PyTorch is a training-only dependency.

Tech stack

What it’s made with

App

  • Next.js 16 (App Router, server actions)
  • React 19
  • TypeScript
  • Tailwind CSS v4 with Steadfast tokens
  • @react-pdf/renderer
  • Lucide icons

Data and services

  • Neon Postgres
  • Prisma 7 with the Neon driver adapter
  • OpenAI Responses API, structured outputs
  • Resend

Underwriting engine

  • PyTorch 2 for training
  • numpy for serving
  • Python 3.12, FastAPI, uv
  • lifelines as the Cox benchmark
  • SOA 2015 VBT and 2017 CSO tables

Open data

  • CDC NHANES 1999–2018
  • NCHS linked mortality files (National Death Index)
  • Society of Actuaries mortality tables
  • Published rate charts for calibration

Quality and delivery

  • vitest, 450 tests
  • zod on every boundary
  • Vercel, two services
  • pnpm

See it for yourself

Answer a few questions and watch the plan build.

Open the planner