All work

Case study · Sep 2026

An AI assistant that actually knows the business it's on

Product ManagerDesignerNo-Code Developer

Timeline

Sep 18–24, 2026, ongoing

Team

Solo

Status

Live in production

Stack

Next.js · TypeScript · OpenAI · Redis · Stripe

Visit Nelo

Challenge

Small businesses want an AI assistant that actually knows their business — not a generic chatbot that hallucinates, and not a custom build that needs an engineering team they don't have. And even a good one is worthless if it takes more than an afternoon to put on a real website.

Solution

A widget any business installs with one script tag, grounded only in their own catalogue and content, built and shipped solo by directing an AI coding agent through every product, design, and architecture decision.

Outcome

Live in production in about a week — priced, multi-tenant, and already running on a real install that surfaced two genuine product gaps within a day of watching someone actually use it.

~1 week
from an empty repository to a priced, live product
5
industries shipped with their own default assistant behavior
Nelo widget open on a live websiteNelo widget conversation example

Context

A chat widget that actually knows the business it's on

Most small businesses that want an “AI assistant” on their site are choosing between two bad options: a generic chatbot that hallucinates answers about their products, or a custom build that needs an engineering team they don't have. Nelo is the middle path — a widget a business owner installs in minutes, that answers only from their own catalogue and documents, never invents a discount or a fact, and hands off to a human the moment it's out of its depth.

Four decisions ended up shaping everything else: how a business actually puts this on their website, how the AI decides what to say and how to say it, how a business teaches it anything about itself in the first place, and what happens when someone tries to abuse it. I made every one of those calls myself, then directed an AI coding agent to build, test, and ship the result — the same loop whether the decision was a product call, a design call, or an architecture call.


The problems

Four decisions everything else depended on

These weren't implementation details — get any one of them wrong and the product doesn't work, no matter how good the model answering questions is.

Distribution — Getting onto someone else's website

A business isn't going to hire a developer to add a chat widget, so the entire install had to be one script tag — no build step, and no way for it to touch the host page's own styles. That constraint set the architecture: the widget lives in an iframe and only ever talks to the host page through postMessage, and it exposes one small public API — window.Nelo.open() — so a business's own buttons can open the widget and hand it a specific set of starter questions. Getting the handshake right mattered more than it sounds: the iframe has to tell the host page it's actually ready before the host page can safely talk to it, or the first click right after the page loads silently does nothing.

AI & conversation — Being useful without sounding automated

Every reply sits on a tension: say everything correct and you get a hedge-everything wall of text nobody wants to read; sound warm and brief and you risk skipping the one detail that actually mattered. A couple of small rules close most of that gap — replies break into short paragraphs instead of one dense block, and they type out at a pace just ahead of normal reading speed instead of dumping instantly, so the assistant feels present without making anyone wait.

But the harder version of this problem isn't tone, it's action. The only place Nelo had ever captured a visitor's contact details was a retail-only “email me when it's back in stock” flow, so for any business outside retail, a visitor who wanted to be connected or followed up with had no real way to leave their details — at best the assistant could recite the business's own contact information back to them, which isn't the same as capturing a lead. I generalised that mechanic into an industry-agnostic handover the AI is structurally required to trigger — not just describe in words — the moment a visitor wants to be connected.

Sounding helpful and doing the useful thing turned out to be two different features. I had to design for both, separately.

Architecture — Teaching an AI about a business it's never seen

The hardest architecture problem wasn't the model — it was designing how a non-technical owner hands their AI everything it needs to know, without turning them into a prompt engineer. Three layers do this: a structured catalogue (products, specs, stock status, with CSV import/export so switching from a spreadsheet is a five-minute job), free-text context documents for whatever doesn't fit a product row (policies, FAQs, a résumé), and an industry preset that seeds sensible default instructions the owner can then edit in plain English. All three get reassembled into the system prompt fresh on every request, so a catalogue edit takes effect on the very next message — nothing to redeploy.

That design worked well right up until a tenant had no product catalogue at all — a personal site, a services business. The very first thing a visitor sees, the greeting and the sample questions, had never actually been generated from that context; it was templated, and the template only knew about products. The fix was making that first impression get generated the same way every real answer already was — from the tenant's own content, cached and only regenerated when that content changes. For a personal website, that meant tuning the questions toward skills and experience rather than narrated biography, since the visitor asking them is usually sizing someone up for a role, not reading a memoir.

The hardest architecture problem was never the model. It was designing how someone who's never touched a CMS teaches an AI everything it needs to know.

Security — The key is public, on purpose — plan for it

Nelo's embed key is public by design, sitting in plain HTML on every customer's site, the same way a Stripe publishable key or a Google Maps key is. That's normal, but it also means anyone can copy it and hit the chat API directly — running up a business's own OpenAI bill, or scraping their catalogue. I audited how comparable products handle this and designed a layered defence rather than a single fix: per-IP rate limiting on the chat endpoint, binding a key's traffic to the domains it's actually embedded on, and a hard per-tenant spend cap so the worst case stays bounded no matter what gets past the first two layers.


What we built

Four surfaces, one product

Each one exists because of one of the problems above — none of it is generic SaaS scaffolding.

A dashboard for feeding and watching the AI

Everything an owner needs to teach their assistant something, see what it's actually been asked, and change how it looks and behaves — without a developer and without touching the widget's own code. Context holds the catalogue, the documents, and the editable instructions; Interactions shows real conversations, logged enquiries, and every lead the handover flow has captured; Settings covers the widget's name, color, the fixed set of links it's allowed to offer, and the embed key itself; Billing sits on top of Stripe.

A marketing site you can talk to

The homepage doesn't just describe what Nelo can do — a visitor can open the real widget from the page itself and ask it real questions about Nelo before ever signing up. That's the distribution mechanic from the problems above, turned into the product's own acquisition funnel: prove it by letting someone use it, not by writing more marketing copy about it.

The widget itself

A small, self-contained React app that runs inside every customer's iframe — the branded chat interface, the typing animation, the sample questions, and the tool-driven UI (link buttons, restock forms, lead-capture forms) a visitor actually sees and clicks. It's a separate build from the dashboard, deliberately: it can't depend on anything from the host page, has to render correctly above any stacking context a business's own site throws at it, and ships and iterates on its own.

A superadmin console across every account

Separate from the customer-facing dashboard entirely, gated to a small admin allowlist: search any tenant by customer email, business name, tenant ID, or API key; inspect their full config, Clerk org membership, and Stripe linkage; pull their raw embed key and recent enquiries and restock requests; and bulk-resync plan limits from Stripe across every account at once.

Nelo dashboard — catalogue tabNelo dashboard — widget configNelo dashboard — embed codeNelo dashboard — interactions

Build

No-code, not no engineering

I don't write the code by hand. I direct an AI coding agent the way a PM directs an engineering team — except the loop from “here's what should happen” to a reviewed, running change is minutes, not sprints.

The practice

Every part of Nelo — the data model, the Stripe integration, the widget's CSS, the copy in the system prompt — was produced by describing the intended behavior in plain language and reviewing the actual result: the real diff, then the real running app. Nothing shipped on “it compiles.” A typical change went through a type-check, a lint pass, and a production build before I'd even look at it in the browser — and then I'd click through the actual flow, submit the actual form, read the actual page, often against a live tenant's real data, not a mock.

Keeping the AI honest with tools, not prose

Anything the assistant does that changes state — showing a link button, opening a restock-alert or lead-capture form, logging a summary of the conversation — goes through a structured tool call, never free text. The model can only ever pick a link from the business's own configured list, because the tool's schema is built from exactly that list; there's no path for it to invent or misroute one. Forcing state changes through tools instead of prose is what turns “never show a link that isn't real” from a hope into a guarantee.

Billing, wired without writing Stripe code

I set up a full Stripe sandbox — products, two priced plans, a webhook, the CLI authentication — entirely through the same conversational process, then verified it by actually clicking “Get Starter” in the running app and watching the real webhook fire, rather than trusting the dashboard alone.


Timeline

Empty repo to priced product, one week

  1. Sep 18

    Scaffold & core loop

    Multi-tenant data model, the chat API, and the embeddable widget — the smallest version of the whole product, working end to end.

  2. Sep 18

    Onboarding & tenancy

    Clerk-based sign-up and organization creation, auto-provisioning a tenant the moment a business signs up.

  3. Sep 18

    Catalogue, config & enquiry log

    In-app catalogue with CSV import/export, widget configuration, and an enquiry log with CSV export — dropped an early Google Sheets read-path once the in-app version could fully replace it.

  4. Sep 18

    Restock alerts, billing, real marketing site

    The first lead-capture flow (restock subscriptions), Stripe billing wired end-to-end, and a real marketing homepage in place of the scaffold's placeholder.

  5. Sep 19

    Dashboard rebuilt around a rail nav

    Restructured the dashboard's information architecture around a left rail, and added per-tenant agent instructions so owners could shape their assistant's tone directly.

  6. Sep 20–23

    Pricing, growth bets, conversational polish

    The security and cost audit, the reprice to $19/$50, the personal-website vertical, CTA link buttons, the transcript scroll fix, a new logo, and the reply-pacing and paragraph-breaking rules.

  7. Sep 24

    Dogfooding fixes, same day

    Watched a real external install, found and shipped the contextual-greeting fix, the skills-focused question tuning, and the generalized lead-capture flow — all found and closed within one day of watching someone actually use the product.

What holds it together

Three rules the whole system follows

—

Grounded, not generative

Every answer comes from the tenant's own catalogue, documents, and instructions — never invented, never assumed.

—

One script tag

No build step, no SDK, nothing that can touch a host page's own styles or scripts.

—

Actions through tools, not prose

Anything that changes state is a structured tool call, so a promised link or form is a guarantee, not a hope.


Outcomes

Where it stands

4 surfaces shipped
Customer dashboard, marketing site, the widget itself, and a superadmin console — all in production
$19 / $50
Starter / Pro, with Stripe billing wired end-to-end in test mode
5 industry presets
Retail, food & beverage, services & bookings, SaaS, and personal websites — each with its own default assistant behavior
Real external installs
At least one live deployment by someone outside the project, which surfaced two genuine product gaps — the most useful kind of feedback

From an empty repository to a priced, multi-tenant product in roughly a week, with the same build-review-ship loop still running daily. The real test of the pricing and the personal-website bet is still ahead: getting it in front of people who aren't me.