Insurers' bots handle policies, but they don't sell. I built the one that owns the stage nobody advises
I didn't start with a product idea, I started with a question: how does a large insurer sell you a policy today? Answering it seriously required two things: depth on one concrete case and breadth across the rest of the industry. As the case to study in depth I picked a leading insurer in the Argentine market, La Caja, and surveyed its entire public website: the catalog, the plan structure for each line, the service channels and their roles, the quote forms (auto, home, motorcycle), the help center. All of it with public information, keeping what I observed separate from what I assume.
The customer journey map came out like this:
| Funnel stage | Who owns it today | How it feels |
|---|---|---|
| Understanding what suits me | Nobody, clearly | The customer compares alone |
| Getting a quote | Web quote form | A form, and you have to leave for the web |
| Buying | Web / human agent | |
| Using and managing (post-sale) | Letizia (WhatsApp) + app | Well solved |
Both ends are covered. The middle is not. And the most interesting finding of the benchmark: La Caja has already proven WhatsApp works for them (they invested in Letizia, they offer telemedicine through that channel). The channel is validated internally; they use it for the wrong stage of the funnel.
The breadth came from the rest of the landscape: the case was the anchor of the analysis, not its limit. Every flow decision was checked against the full industry: quote forms from other insurers and aggregators (Mercantil Andina, comparaencasa, asegurarmiauto) for question design; abandoned-cart recovery practices from conversational commerce for re-engagement; the real WhatsApp Business rules (the 24-hour window, templates, opt-in) for everything proactive; and industry chatbot timeout patterns for handling silence. When I say below that "no quote form asks this", it's because I looked at them, not because I assume it.
The rule is not to compete where the incumbent is already strong. Building another post-sale bot would be effort without a differentiator; conversational pre-sale attacks the moment of highest purchase intent and least support. The bet: advise, compare and quote inside the chat, and deliver a qualified, warm lead. The bot doesn't issue policies or replace the official rating engine: its ceiling, chosen on purpose, is leaving the sale ready to close.
Before the features, the principles that filtered every decision:
Quoting is a structured, repeatable process (web quote forms already model it in four steps); questions are open-ended ("what's a deductible?", "does it cover hail?"). Menus alone would be rigid; AI alone would be unpredictable and expensive exactly where mistakes cost the most, which is in what the bot claims about coverage. So: deterministic menus for the quote flow, and the language model for open questions, anchored to a knowledge base of real content surveyed from the site, so it doesn't make things up. The trade-off is maintaining two paths and tuning the boundary between them, and a good part of the decisions that follow are, precisely, refinements of that boundary.
The initial version of the auto flow asked for the year and then, as a separate step, "is it brand new or used?". A real user showed me the problem in the worst way: for a 2009 car that question has an obvious answer, and asking it anyway makes the bot look like it can't reason. Before touching the flow I benchmarked how real quote forms solve it (La Caja, aggregators, comparison sites): none of them asks for the condition separately. The year carries it implicitly, or "brand new" is an option inside the year selector.
v1: condition as its own step
"What year is the car?" and then "is it brand new or used?". For a 2009 the second question has an obvious answer: the bot adds a step and looks like it can't reason, right at the start, where drop-off is highest.
v2: the year carries the condition
A past year implies used, and the flow moves on without re-asking. Only the current year is ambiguous, and there the follow-up uses the business's objective criterion: "is it registered yet?" (brand new means exactly not-yet-registered). A yes-or-no fact instead of a judgment call.
The flow went from 8 to 7 steps for the common case, and the follow-up for the ambiguous case changed from a subjective judgment to a verifiable fact. What I didn't build was also a decision: I don't ask for the registration year as a separate data point, because it doesn't move the price (the insured value comes from the model year against the market value table) and it would add a step against everything I had been simplifying.
The original menu had "Talk to an agent" as a visible option. The problem: it's the easy way out, and a good share of users would pick it upfront, so the bot cannibalizes itself (it never gets to quote or qualify the lead, which is its job). The solution wasn't hiding the human, it was changing the trigger: the agent is offered when the emotion engine detects distress, with a level. The classifier returns a label, not an intensity, so the level comes from severity and persistence: anger weighs double and triggers the offer at once; frustration needs to persist beyond one message, because one can be venting and two are a signal. A satisfied message resets the accumulator.
The exits are kept deliberately: typing "agent" always routes to a human (whoever wants a person gets one, just without promotion), and the post-quote closing keeps "have an agent call me" as the main option, because there the agent is the conversion, not a leak. The honest trade-off: a user determined to talk to a person takes one extra message to find out how.
The menu started with seven options and ended with four symmetric quote actions. The three prunings ("I have a question", "Talk to an agent", "Compare plans") distilled a principle: the menu lists actions, not information or escape hatches. A question gets answered by typing it; the plan comparison appears where it actually helps (at the final choice, with the price in front of you); the agent is offered by context. Around the menu, the other friction refinements piled up: a progress indicator ("Step 3 of 7") because asking blind is a known cause of drop-off, the ability to type "back" to fix the last answer without losing progress, and a parser that understands "I think the second one works for me" as option 2, with a conservative rule: if the message smells like a question, the bot prefers to re-ask rather than choose for you.
The moment of highest purchase intent is right after seeing the price, and the first version wasted it: it quoted and ended with "type menu for another query". The current closing pushes forward: have an agent call me (with contact capture and the quote summary, so the agent calls warm and with context), buy online, or compare another plan. The contact step also asks for an explicit, separate opt-in for WhatsApp re-contact, because the platform's policy requires affirmative consent for marketing messages, and the re-engagement design (resuming a half-finished quote, a proactive "still there?", a template outside the 24-hour window) is built on WhatsApp Business's real rules, not on how I'd like them to work.
The part I most want to show: the AI pieces are not assumed, they are measured.
The engine has two distinct semantic pieces, and each solves a different business problem:
RAG (the LLM anchored to a knowledge base). The problem it solves is trust: an insurance assistant that invents a coverage isn't a demo with a bug, it's a risk. So the model doesn't speak from memory: it answers over a base of real surveyed content (the plans, the deductible with an example, the requirements per line). The benefit here isn't cost, it's fewer invented answers in the industry where an invented answer costs the most.
The embeddings router (retrieval without generation). The problem it solves is cost, latency and determinism, and here there are numbers. The starting observation: most open questions are the same few doubts (what does this plan cover, what's a deductible, how do I pay). Paying for an LLM generation on every repetition is burning money, because every open question drags the whole knowledge base into the prompt:
| Route | What it processes | Cost per query |
|---|---|---|
| LLM (generation) | ~5,500 input tokens (knowledge base + history) + ~180 output | ~$0.02 |
| Router (embedding + cosine) | ~30 embedding tokens, no generation | ~$0.0000006 |
With the router resolving 64% of known questions without touching the LLM, the savings projection scales with volume (assumptions: the per-query cost above, at the API's list prices):
| Open questions / month | Resolved by the router | Estimated monthly savings |
|---|---|---|
| 10,000 | ~6,400 | ~$130 |
| 100,000 | ~64,000 | ~$1,300 |
| 1,000,000 | ~640,000 | ~$13,000 |
At demo scale this is pennies, and I'll say that plainly. But cost is only a third of the argument. The other two weigh as much or more for an insurer: latency (the canonical answer comes back in milliseconds; a generation takes seconds, and in a chat the wait is felt) and determinism: the repeated question is always answered with the same canonical text, reviewable and approvable by compliance, with zero hallucination risk exactly on the questions answered most often. In insurance, that last one is worth more than the savings.
The router compares each query against a corpus of 37 known questions (181 phrasings) and returns the canonical answer only if similarity clears a threshold. Choosing that threshold is a binary classification problem (does this query have a known answer?), so it was measured as one, with leave-one-out and 31 distractors as negatives:
The chosen point favors coverage with the LLM as the safety net: it resolves 64% of known questions with 97% content accuracy in what it answers. And with an industry-specific criterion: for an insurance bot, a wrong canned answer is worse than spending a call.
The emotion classifier followed the same discipline: it was evaluated against a labeled benchmark of my own. The version with the large model scored macro-F1 0.91 but was a latency drag; the small model scores 0.87 without losing the actionable signal (it detects all 20 negative messages in the set, with zero false positives), and costs a third per token on top. The decision was the small model, with the eval in hand.
And I learned a maintenance lesson I didn't see coming: when I changed the auto flow, the canonical answer for "what info do you need" went stale and the router kept serving it with full confidence. Retrieval doesn't find out the product changed underneath it: now every flow change triggers a pass over the corpus and a re-run of the eval, because the threshold gets revalidated, not assumed.
The funnel is also instrumented end to end: quote start and progress events, lead captured, quote
accepted (with channel: agent or online), handoffs, where people get stuck (flow_stuck, with the
exact step) and even where they correct an answer. The dashboard lives at /funnel, in the same demo.
It's a pre-sale bot. It doesn't issue policies: the binding price and issuance come from the official rating engine and underwriting, with regulatory requirements it wouldn't be right to simulate. The prices it shows are estimate ranges from a factor model of my own, anchored to the real surveyed prices, and they are presented as what they are. The chosen ceiling is delivering a qualified, warm lead, with its quote summary, ready for an agent to close. Defining that ceiling early organized everything else: what to build, what to measure and what not to promise.
Technical knowledge here is a means, and it shows in what it enabled. The architecture is hexagonal (the core doesn't know the infrastructure), and that paid off in product terms several times: the bot quotes four lines with four different pricing models (auto by factors, home branching by owner or tenant, personal accidents as a published-price catalog, bikes as a rate over declared value) without touching the conversation engine; the same core serves two channels (the WhatsApp webhook and a web demo styled like WhatsApp, so anyone can try it without an allowlist); and the identity is white-label: the brand is a configuration variable and the catalog is a folder of markdown, so switching insurers means changing content and configuration, not code. The demo runs with a neutral identity on purpose: it isn't right to show up wearing another company's logo, and an insurtech looking at it should be able to picture itself using it.
TypeScript end to end with hexagonal architecture verified in CI, Hono as the server, Claude (AI SDK) for generation and emotion classification, OpenAI embeddings for the semantic FAQ router, Postgres for leads and events, Redis for sessions, Docker and continuous deployment with a public demo.
The demo is live: insurance-advisor-bot.onrender.com (free tier: the first load may take a few seconds to wake up). Type "hola" and the menu starts (the bot speaks Spanish; it was built for the Argentine market). The full analysis, with 24 documented decisions and the benchmarks, is in the repository.
I'm publishing this to have it challenged:
If any of this made you think differently, write to me.
At a glance
How does an insurer sell you a policy today? I set out to answer that question with public data and found a pattern that repeats across the industry: post-sale is well solved (WhatsApp bots, apps, self-service) and quoting lives in cold web forms. The middle, advised sales (the moment when someone doesn't know whether basic or full coverage suits them), has no conversational owner. I built the bot that owns that stage: it compares plans, advises, quotes four product lines and captures the lead inside the chat. To ground the analysis in real data I picked a concrete case study, La Caja (their bot Letizia is one of the good ones, and even so it serves, it doesn't sell), but the engine is white-label: the brand is configuration and the catalog is content, so it works for any insurer. Every decision is documented with its criteria, its data and its trade-off, and the AI pieces are not assumed to work: they are measured (a ROC curve for the FAQ router, a macro-F1 eval for the emotion classifier). It is deployed as a public demo.