MSP operations platform · Live in production

Warp

The system my IT company runs on: tickets, billing, documentation, security work, phones and the client portal, in one place instead of six.

Role: Product design, flow design, and direction of the agents that wrote it.

Timeline: Ongoing — in daily production use

Database tables
110

Table definitions in shared/schema.ts (3,748 lines). Counted in the repository on 8 August 2026.

API endpoints
671

Registered route handlers across 9 route modules. Counted in the repository on 8 August 2026.

Lines of TypeScript
~190k

331 .ts/.tsx files excluding node_modules and dist. Counted in the repository on 8 August 2026.

Test files
47

Vitest files colocated with source. Counted in the repository on 8 August 2026.

Commits
3,169

git rev-list --count HEAD, Counted in the repository on 8 August 2026.

What was actually broken

A managed IT company runs on a stack of tools that don't know about each other. Ticketing in one, invoicing in another, documentation in a third that nobody updates, security compliance in a spreadsheet, and the phone system off to the side entirely.

The cost isn't the licences. It's the seams. Time gets worked and never billed because it lived in the wrong tab. Documentation goes stale the moment the person who wrote it fixes the thing a different way. A client asks "what did you actually do for us last month" and answering honestly takes an afternoon.

Warp is what happens when you stop integrating those tools and build the thing they were all approximating.

The flow I designed

The whole platform is organised around one question a technician asks constantly: what should I be working on right now?

Almost every design decision follows from taking that question seriously.

The day starts with what's rotting, not with what's newest. Tickets that have gone quiet get pinned to the top of the queue rather than sinking down it. A list sorted by recency is a list that hides its own failures.

Snoozing is a first-class action. "Not now" is the most common honest answer about a ticket, and every system that lacks a word for it gets that answer expressed as an ignored row instead. Snoozed work comes back on its own.

The phone never lives in a different place. The softphone is mounted above the router, so a call survives navigation. This is the single most-felt decision in the product — see the note on the screenshot below.

Clients get a door, not a login. The client portal is reached by a signed link. No password to set, no password to forget, no support ticket about the password for the ticket system.

Documentation is a by-product of the work, not a second job. When a ticket resolves, the system drafts the documentation from what actually happened. Nobody has ever reliably written docs as a separate task, and building as if they would is a design failure dressed up as a process problem.

Interface notes

  1. The sidebar is mounted once

    It lives above the router rather than inside each page, so moving between tickets, billing and documentation never tears it down and rebuilds it.

  2. The phone has a permanent home

    Softphone, notifications and the assistant occupy a fixed strip. A technician on a live customer call can navigate the entire product without the call ever dropping — the single most-felt decision in the product.

  3. Snooze is a first-class control

    "Not now" is the most common honest answer about a ticket. Systems without a word for it get that answer expressed as an ignored row instead; here it comes back on its own.

  4. The queue answers a question

    Instead of a list to scan, the right-hand panel says what to do next and why. Ranking is the product; the list is just the evidence.

The hard part

Getting the auto-documentation to fire exactly once.

The interesting part was never the writing. It was that "when a ticket resolves, draft the doc" is a deceptively hard sentence.

A ticket can be resolved twice. Two processes can see the same resolution. A deploy can restart the server mid-draft. Each of those turns a helpful feature into either duplicated garbage documentation or silently missing docs — and missing is worse, because nothing looks broken.

The solution is an atomic claim: a worker takes exclusive ownership of a resolution before it does any work, so a second attempt finds it already claimed and stops. Anything claimed but not finished — the deploy-mid-draft case — gets swept and retried on a timer. It is not clever code. It is the boring, correct answer, and getting to boring-and-correct is most of the actual job.

The general shape of this shows up in every serious project: the feature is easy, the guarantee is hard.

Stack

FrontendReact 18 + Vite, Wouter, TanStack Query
UIshadcn/ui on Radix, Tailwind, framer-motion, TipTap, dnd-kit
BackendExpress + TypeScript, bundled with esbuild
DatabasePostgreSQL (Neon) + Drizzle ORM
AuthTwo systems: session + TOTP 2FA for staff, signed tokens for the client portal — Staff and clients have genuinely different threat models; sharing one auth system would have compromised both.
PaymentsStripe, with Bill.com invoice sync
EmailResend, SendGrid, MailerSend and Gmail API, behind one interface
TelephonyTwilio voice, SMS and video — softphone, IVR, voicemail
AIClaude primary with an OpenAI fallback, behind a single aiChat() call
StorageGoogle Cloud Storage
TestingVitest, colocated with source
HostingReplit autoscale + Neon Postgres

Outcome

Warp runs a real business every day. It replaced the ticketing system, the invoicing, the documentation wiki, the security-compliance spreadsheets, and the phone system.

It is multi-tenant throughout — every organisation-scoped request verifies ownership — so it is built to run more than the one company currently on it.

All work · Start a project