Internal CRM for an AI agency · Belarus
GoodDayAI CRM
Our own CRM for our own pipeline — on the same database as the site and the bots
The task
The agency already had a public site with a booking form and a Telegram bot, both writing clients into one Supabase table. What was missing was a working tool on top of that data: a sales pipeline with clear stages, analytics on money and where deals get stuck, and a way to track combined profit and split it between co-owners — without ever putting real client names and deal sums on the public site or in a bot that outsiders can reach.
Problems we solved
Client and deal data can’t sit on the public site, but the pipeline has to live somewhere.
A separate Next.js project on its own Vercel deployment and domain: the public site has no route that ever returns client data. Sign-in is a password behind an HMAC-signed httpOnly cookie, checked in middleware on every request, with anti-bruteforce, `noindex` and a strict CSP.
Deals move through 15 different stages — from first contact to renewal or pause — and it’s easy to mix up what follows what.
A kanban board backed by a status machine: every stage has its own set of allowed transitions, and an invalid jump is rejected at the API level, not just hidden in the UI. Cards move by drag-and-drop or a button; filters cover source, amount, "remind today" and stale deals.
A deal’s amount lived in a single field per client — there was no way to separately record a one-off setup fee and a recurring monthly subscription, and a monthly payment was only ever counted once in the report.
Split into two fields: the one-off setup fee counts as income once, when the deal is won; the monthly subscription feeds a separate MRR figure, drawn only from clients who are currently paying (renewal and pause are tracked separately and don’t get mixed together).
The two co-owners’ profit was tracked by hand, with no single place showing income, expenses and each person’s final share.
An "Expenses" section: a shared cash box by category across several currencies, profit = income from won deals minus expenses, each co-owner’s share set by a configurable percentage editable right in the UI.
Some clients come through franchise partners, and those partners must not see other clients or the shared cash box.
Multi-tenancy keyed on a `tenant` field: each franchise has its own account and password, sees only its own clients, and the owner’s expenses and analytics sections are unreachable for it — isolation is enforced in every API route, not just by hiding a button.
A missed call or a forgotten follow-up slips through the list of leads unnoticed.
Several independent "follow up" reminders per client, each with its own note; status changes and other actions are written to a `crm_log` audit trail with the name of whoever made them.
What we did
- A 15-stage kanban board with validated transitions and drag-and-drop
- Client card: services, a one-off setup fee and a monthly subscription as separate fields, requisites, a note, an action history
- Analytics: win rate, MRR, a stage-by-stage conversion funnel, average deal size, "where deals get stuck"
- Expenses and profit: a shared cash box by category and currency, each co-owner’s share configurable in the UI
- Franchises: separate partner accounts with client isolation by `tenant`, enforced in every API route
- Referral programme: personal gift links for friends, the reward granted only after the friend’s first payment
- AI on top of the pipeline: a draft message to the client, natural-language Q&A over the whole deal base, lead scoring with a suggested next step
- Several independent "follow up" reminders per client and a shared action log naming who did what
- A separate hidden Vercel deployment, a password behind an HMAC cookie, anti-bruteforce, `noindex` and a strict CSP
How it looks

