How I built colet.app — behind the scenes
How I built colet.app — from zero to a working SaaS in months
People ask me how I built colet.app as a solo developer. They expect some clean, linear story. The truth is messier, more interesting, and has a lot more debugging than anyone wants to admit. This is the real story of how I built colet.app — the decisions, the mistakes, and the parts where AI genuinely changed the game.
The problem that started everything
Romanian courier companies running routes between Romania and the UK were managing everything with spreadsheets, WhatsApp groups, and phone calls. I'm not exaggerating. A dispatcher would receive a transport request via WhatsApp, write it on paper or paste it into a Google Sheet, then call the driver with directions.
This created obvious problems:
- Orders got lost between WhatsApp messages
- Route planning was gut-feel, not optimized
- Clients had zero visibility on where their package was
- Billing was a nightmare — tracking who paid what, when, for which shipment
I saw this firsthand working with Ami Tour, a transport company on the Romania-UK corridor. They needed something purpose-built. Not a generic logistics tool. Something that understood their specific business: multi-stop routes across countries, cash-on-delivery, depot management, and client communication primarily through WhatsApp.
Choosing the tech stack
This is where most "how I built it" articles get boring. I'll keep it practical.
Frontend: React + TypeScript + Vite
Not Next.js this time. colet.app is a dashboard-first product. There's no SEO requirement for the admin panel. SSR would add complexity without value. Vite gave me fast builds and hot reload. React 18 because the ecosystem is unmatched for complex UIs — and I had years of muscle memory with it.
Styling: Tailwind + shadcn/ui. For a SaaS dashboard with hundreds of UI states, utility-first CSS wins. This isn't a marketing site where custom design matters for every pixel. Speed of building matters.
State management: Zustand for global state, TanStack Query for server state. This combination is criminally underrated. Zero boilerplate, predictable updates, and TanStack Query handles caching, deduplication, and background refetching automatically.
Backend: Supabase
This was the most consequential decision. Supabase gave me:
- PostgreSQL with Row Level Security (RLS) — multi-tenancy baked into the database layer
- Edge Functions for serverless logic
- Realtime subscriptions for live driver tracking
- Auth out of the box
- Storage for documents and proof-of-delivery photos
Could I have built this with a custom Node backend? Sure. It would have taken 3x longer and I'd still be writing auth middleware.
Mobile: Capacitor
The drivers need a mobile app. React Native was the obvious choice, but I went with Capacitor — same React codebase, wrapped for iOS. One codebase for web dashboard + mobile app. The tradeoff is performance on heavy animations, but for a logistics app with maps and forms, it's more than enough.
Maps: Google Maps Platform
Google Directions API for route optimization, Places API for address autocomplete, Maps JavaScript API for the dispatch view. Expensive at scale, but nothing else comes close for multi-country routing accuracy.
The multi-tenant architecture
This is the part I'm most proud of and the part that caused the most headaches.
colet.app serves multiple courier companies from one codebase. Each organization sees only their data — their orders, drivers, clients, routes. But the super admin (me) can see everything, impersonate any organization, toggle features per tenant, and audit everything.
How it works
Every table in the database has an organization_id column. Supabase RLS policies enforce that users can only read/write rows matching their organization. This happens at the database level — even if my application code has a bug, the data isolation holds.
-- Simplified RLS policy
CREATE POLICY "Users see own org data" ON orders
USING (organization_id = auth.jwt() -> 'org_id');
The tricky parts were:
- Super admin bypass — needed a clean way to override RLS without weakening it
- Cross-org reporting — aggregate metrics without exposing individual org data
- Tenant-specific features — feature flags per organization, not just globally
- Branded tracking pages — each company gets their own public tracking URL with their branding
What I'd do differently
I'd invest more time in the tenant provisioning flow upfront. Adding a new organization currently requires several manual steps. Should have built a self-service onboarding from day one.
The WhatsApp AI agent
This is where things got interesting. Courier companies live on WhatsApp. Their clients send messages like "I need to send a package from Suceava to London, 15 kg, pick up tomorrow."
I built a conversational AI agent using Claude Haiku that:
- Receives the WhatsApp message via WhatsApp Cloud API
- Extracts order details (origin, destination, weight, pickup date)
- Asks clarifying questions if needed
- Creates the order in the system
- Sends confirmation back to the client
This replaced hours of manual data entry daily. A dispatcher used to spend their morning copying WhatsApp messages into the system. Now it happens automatically.
The AI isn't perfect — it handles about 80% of messages without human intervention. The other 20% get flagged for manual review. But that 80% represents massive time savings.
Using AI to build faster
Here's where I'll be honest, because there's too much hype around AI in development.
I used Claude Code extensively throughout the build. Here's what it actually helped with:
Where AI saved me weeks:
- Writing RLS policies and testing them — tedious, error-prone work
- Generating TypeScript types from database schemas
- Writing 274+ automated tests (Vitest)
- Boilerplate for new features — CRUD operations, form validation, API routes
- Refactoring large components into smaller ones
- Writing Edge Functions for webhook handling
Where AI was useless:
- Architecture decisions — multi-tenant data model, real-time strategy, caching approach
- UX flows — how should a dispatcher plan a route with 25+ stops?
- Business logic — pricing rules, delivery status workflows, payment reconciliation
- Debugging production issues with incomplete context
The honest ratio: AI wrote maybe 40% of the code by volume. But the 60% I wrote contained 90% of the hard decisions. AI is a multiplier, not a replacement. A senior developer using AI builds faster. A non-developer using AI builds nothing that works in production.
Real metrics — first month
I'm not going to inflate numbers. Here's where colet.app stands after the first month of real usage:
- 4 organizations actively using it (Ami Tour, Wir Spedition, BICORAS, and one more)
- 42 users — admins, dispatchers, and drivers
- 368 orders processed
- 250 orders delivered (68% delivery rate)
- 54 routes created with 702 stops
- 819 clients in the database
- 9 depots configured
These aren't vanity metrics. They represent real packages, moved by real drivers, across real borders. The system handles the full lifecycle: order creation (manual, API, or WhatsApp), route optimization, real-time driver tracking, proof of delivery, and billing.
The mistakes I made
Mistake 1: Building too many features before launch
I spent months building the financial module (billing, payments, outstanding balances) before a single user had signed up. Should have launched with orders + routes + tracking and added billing when users asked for it.
Mistake 2: Over-engineering the real-time system
The live dispatch map with real-time driver positions is cool. It's also expensive (Supabase Realtime + GPS pipeline + geofencing). Most dispatchers check it a few times per day, not continuously. A 30-second polling interval would have been fine for v1.
Mistake 3: Underestimating onboarding
Getting a new courier company set up requires configuring depots, importing clients, setting up drivers with the mobile app, and training dispatchers. I assumed they'd figure it out. They didn't. I now spend significant time onboarding each new customer.
Mistake 4: Not charging from day one
I gave the first few organizations free access to get feedback. Good for learning, bad for establishing that the product has value. Should have charged even a small amount from the start.
What I'd do differently today
- Launch with 30% of the features — orders, routes, tracking. That's it.
- Build onboarding first — self-service setup with templates and guided flows
- Charge from day one — even if it's a reduced "early adopter" price
- Mobile-first for drivers — I built the web dashboard first, but drivers only use the mobile app. Should have started there.
- API documentation earlier — some clients want to integrate their existing systems. The API was an afterthought.
What's next
Subscription billing is in progress (Netopia for payments, SmartBill for invoicing). The iOS app is heading to the App Store. Android build is queued. Push notifications need to go native instead of through web push.
The bigger vision: colet.app becomes the default logistics platform for Romanian courier companies operating international routes. The corridor is underserved, the competition is using 15-year-old software, and the companies are growing fast.
If you want to see the finished product, check out the colet.app case study on our portfolio.
The takeaway
Building a SaaS solo is possible. Building it well requires taste — knowing what to build, what to skip, and when to ship. AI made me faster at writing code. It didn't make me faster at making decisions. The hard parts of building software are still hard. But the tedious parts? Those are mostly solved now.
The best code I wrote for colet.app is the code I didn't write — the architecture decisions that prevented entire categories of bugs, the features I said no to, and the simplifications that made the system maintainable by one person.
If you're thinking about building a SaaS product, start smaller than you think. Ship earlier than feels comfortable. And use AI for the boring parts so you can focus on the parts that actually matter.