An AI customer-conversation platform for small businesses — every channel in one inbox, an AI that answers from the business's own knowledge base, and a handoff rule that stops it from bluffing.
A small business gets asked the same twenty questions all day — opening hours, prices, availability, "do you deliver to my area" — spread across WhatsApp, the website chat, and email. Nobody is watching all three at once, so the fast answers go out at 11pm or not at all, and the leads that were actually worth something cool off in a queue nobody triaged.
The obvious fix, a generic chatbot, tends to make it worse: it will confidently invent a price rather than admit it doesn't know one.
Replai unifies WhatsApp, web chat, and email into a single inbox, answers what it can, classifies what came in, and flags the hot leads. The design constraint that shaped everything else: a confident answer that isn't grounded in the business's own knowledge base is the exact failure the product exists to prevent — so that case had to route to a human, not to a guess.
Replai is an npm-workspaces monorepo: a React + Vite + TypeScript dashboard (Tailwind, right-to-left for Hebrew), an Express + TypeScript API, and a shared types package so the contract between them is checked at compile time rather than hoped for.
Persistence is Postgres through Prisma — Business, User, KnowledgeItem, KnowledgeSource, Conversation, Message, Lead — with channel and status modeled as enums (whatsapp / webchat / email; new / ai_handled / awaiting_human / closed) rather than loose strings. Development runs against local Postgres; production moves to a managed provider by changing two connection strings, because the schema doesn't care which one it's talking to.
The reply engine runs Claude Opus 5 with structured outputs. The business's knowledge base is injected into the system prompt as the only permitted source of fact, and every response comes back with three decision fields alongside the text: whether the answer was grounded in the knowledge base, whether a human is needed, and whether this looks like a hot lead.
The escalation rule reads those fields strictly: a conversation moves to awaiting_human if the model asked for a human or if it couldn't ground its answer. Not knowing is treated as a reason to escalate, not a reason to improvise.
Three operator toggles are enforced at this layer rather than in the UI — turning off auto-reply stops the engine entirely, turning off handoff disables escalation, turning off classification stops lead tagging.
The chat widget drops into any site with a single script tag. Its replies stream as NDJSON, which took first visible words from roughly nine seconds down to about two — and the schema puts the customer-facing reply field first, so the internal decision fields arrive after the text the visitor is reading, never before it.
Because the widget endpoints are public and unauthenticated by design, they carry real limits: five conversations per IP per day, thirty messages per conversation, two thousand characters per message, and an origin allowlist controlling which sites may embed it. IP addresses are stored hashed, never raw. If the AI call fails outright, the visitor gets a holding reply — their message is already saved and already flagged for a human.
Escalations send one email through Resend with the caller's name, what they asked, why the AI stepped back, and a deep link to the conversation. One email per escalation, not per message: a timestamp field is claimed before sending so two concurrent requests can't double-notify, and it resets only once a human actually replies. Notification failures are swallowed and logged — a broken email must never take down a customer reply.
The dashboard is protected by a signed httpOnly session cookie whose signing key is derived from the dashboard password, so rotating the password invalidates every existing session for free. Deploying that safely had a sequencing trap worth writing down: Vercel's platform protection is all-or-nothing, so it has to be off for the public widget to work at all. The password must therefore be set and verified first, and platform protection dropped only after — otherwise there's a window where the dashboard is simply open. The app now shows a red banner whenever no password is configured, so that state can't pass unnoticed.
Phase 1 is shipped and deployed at replai-murex.vercel.app: the API, the AI engine, and all five dashboard screens — dashboard, inbox, knowledge base, lead board, and automations. The chat panel on this portfolio's home page is that same product, running live, talking to this deployment's public widget API. Next up is the WhatsApp Business Cloud API connection and an end-to-end test on a real number.