Kyber CRM

The internal system of a small software studio, built to replace a spreadsheet, a calendar and memory with a single place. It is live at crm.somoskyber.com.br and used every day in real operations.

Role
Sole author, from data model to deploy
Context
Kyber Tech
Status
In production, daily use
Access
Private, internal system
Central do dia do Kyber CRM: navegação com treze módulos, cinco indicadores no topo, fila de tarefas do dia e o painel de leitura escrita por IA
The daily hub. Client names and figures are deliberately redacted.

The problem

A small studio's operation fits in a spreadsheet until it stops fitting. The proposal in one file, the invoice in another, whatever was agreed with each client living in the memory of whoever took the call, and the next step of each deal nowhere at all. The cost does not show up at once: it shows up as a forgotten invoice, a follow-up that never went out, and a client question nobody can answer without digging.

The goal was not to have a CRM. It was to have one place where sales, delivery, finance and support look at the same data, under the same rules about who can see what, and where the day's work is already assembled before anyone asks what to do next.

Four surfaces, one model

One Next.js application serves four very different things over the same data model and the same authorization layer.

  1. 01 Web dashboard Where the operation happens: kanban funnel, daily queue, finance and metrics.
  2. 02 MCP server 70 tools that let an AI agent run the system by conversation.
  3. 03 Token-based public pages Commercial proposal, client portal and project status, opened by link, no login.
  4. 04 Scheduled routines Invoicing, revenue snapshots and a daily briefing, on cron.

The interesting part is not how many surfaces there are, it is that all four share the same business rules and the same per-user isolation instead of each reimplementing its own version. A rule fixed once holds for the dashboard, the agent, the public page and the cron job at the same time.

How it was solved

Per-token identity in the MCP server

All 70 tools run under one endpoint, and who is operating is resolved from the request token. Identity is propagated through AsyncLocalStorage so it cannot leak between concurrent requests. Each partner operates only their own data, under the same rules as the dashboard.

Authorization in the database, not the app

133 RLS policies across 29 tables, with SECURITY DEFINER functions for the composite decisions: who can see which lead, who is an admin. The front end is never the security boundary. Even holding the public key, Postgres refuses.

Prospecting cadence engine

A sequence of four touches with distinct angles and growing intervals. Prioritisation uses signals from the lead: public rating, review volume, digital presence and historical niche. There is a daily cap per person and a global opt-out list. A reply from the lead pauses the automation and hands the case back to a human.

AI applied narrowly, with a brake

Four specific uses: draft the opening approach, draft a proposal from the catalogue, write a follow-up by reading the conversation history, and produce the daily read of the numbers. Each with explicit rules against inventing a price, a deadline or a deliverable, plus a review layer that re-reads the generated text and flags claims the system state does not support.

Multi-user with revenue split

Lead sharing between partners, a percentage per lead and payout calculation, with finance respecting the split. Per-user isolation and revenue split are the same problem seen from two sides.

Automate everything except the last mile

The system never sends a message automatically, on any channel, and that is deliberate. It builds the queue, writes the text and records the send, but the send click is always human.

The reason is operational and legal risk. The official API requires documented consent the base does not have. The unofficial alternatives violate the terms and get the account banned. And the number used for prospecting is the same one that serves the clients who already pay.

Automating the send would save minutes and put the main channel of the business at risk. The trade does not pay. The system is designed around that choice, not in spite of it.

What exists today

Lines of TypeScript
21.133
Files
103
MCP tools
70
RLS policies
133
Tables
29
Migrations
32
Routes
21
Cron routines
3

Of the 21 routes, 17 are protected and 4 are public by token. Ten production dependencies in total: no Tailwind and no component library, styling is inline over CSS variables, with a light and a dark theme.

  • Next.js 15
  • React 19
  • TypeScript
  • Supabase
  • Postgres RLS
  • Realtime
  • Storage
  • Vercel
  • @vercel/mcp-adapter
  • Anthropic SDK
  • @dnd-kit
  • Zod

Want to see the rest of the work, or talk about a project?

Updated 5 August 2026