Pan-India car & bike rental, designed and built end-to-end by Kodrex.
Carowna is an enterprise-grade vehicle rental marketplace connecting customers with cars and bikes across India. The platform serves three distinct user roles, Customer, Vendor, and Admin, each with a purpose-built dashboard, secured by role-based access control, and powered by real-time data through Supabase. Every layer was designed and engineered in-house by the Kodrex team with a sharp focus on billing transparency, trust-building UX, and seamless mobile-first experience.
India's vehicle rental market was dominated by platforms that obscured pricing, buried terms in fine print, and left customers confused about what they owed and when. Vendors had no real-time visibility into their fleet. Admins had no centralised control.
Carowna needed more than a booking form. The brief called for a multi-sided platform with clearly separated role experiences, a billing model users could understand at a glance, and a backend that vendors and admins could rely on daily.
Before a single component was built, the product was mapped, user by user, flow by flow.
We mapped the core journeys for each role: a customer searching by city and date, a vendor listing and managing their fleet, and an admin approving vehicles and monitoring the platform. The billing model, 24-hour day-wise cycles, ₹500 advance payment, driver fee upfront, security deposit at pickup, required its own dedicated logic layer. We audited how competitors handled transparency failures and designed Carowna to explicitly address each one.
The data model came first, a fully normalised PostgreSQL schema
with Supabase Row Level Security policies enforcing role-based
data access. Vendor A can never access Vendor B's fleet or booking
data. The booking state machine was explicitly modelled before any
code was written:
PENDING → REQUESTED → UPCOMING → ONGOING → COMPLETED. Every state transition is triggered by a Razorpay webhook,
never by client-side code.
The booking confirmation screen was the most critical design problem: how do you show a customer exactly what they owe now (₹500 advance + driver fee), what they owe at pickup (rental balance + security deposit), and what happens if they return late, without overwhelming them? The solution was a payment split card with two clearly labelled sections and a plain-language billing walkthrough covering both scenarios.
Filter by city, dates, vehicle type, fuel type, and driver preference
View specs, daily pricing, and driver availability on the detail page
Review the full cost summary, upload documents, and agree to terms
Razorpay checkout. Webhook transitions the booking to CONFIRMED
Balance + security deposit collected at pickup. Return by 23:59
Every technology chosen for a specific reason. No bloat, no compromise on performance or security.
App Router, Server Actions, and hybrid rendering. Static cache for inventory, dynamic for live booking state.
Fully normalised schema, Row Level Security policies enforced per role, real-time triggers for profile sync.
Advance payment collection and refund processing. Webhook-driven booking state transitions, no client-side trust.
OKLCH design tokens for a consistent, accessible dark mode. Custom component extensions for booking-specific UI.
Strict mode, zero implicit-any. Zod validation schemas for all form inputs and API payloads.
Edge JWT decoding eliminates auth API round-trips. Role resolution in under 50ms, flicker-free page renders.
Monorepo-ready workspace configuration for scalable multi-package management.
Client-side data fetching with cache invalidation aligned to server-side revalidation tags.
| Role | Feature | Description |
|---|---|---|
| Customer | Booking Flow | Multi-step booking with vehicle selection, date picker, driver toggle, document upload, full cost summary with payment split, and Razorpay checkout. Every charge visible before confirmation. |
| Customer | Vehicle Browse | Infinite-scroll vehicle listing with sidebar filters (type, fuel, price, driver availability) and a sticky filter bar. Sorted and paginated server-side. |
| Vendor | Fleet Management | Vendors list vehicles with image galleries, RC documents, insurance docs, pricing, and specs. Per-vehicle approval workflow, vehicles go live only after Admin review. |
| Vendor | Driver Management | Vendors register and manage their driver pool with licence uploads, daily rates, availability toggling, and performance metrics. |
| Admin | Platform Dashboard | Platform-wide oversight, revenue charts, booking stats, pending KYC approvals, user management, vendor management, and full vehicle approval workflow. |
| Platform | Receipts & Policies | Auto-generated booking receipts with transaction IDs, payment split, and security deposit disclosure. Inline Privacy Policy, T&C, and Refund Policy built into the app. |
Supabase's PostgREST layer does not support logical OR across columns from different joined tables. A simple booking search by customer name OR vehicle name would fail with a PGRST100 error.
Fix: A multi-step parallel query strategy, fetch matching IDs from the vehicles and users tables separately, intersect with the vendor's fleet for tenant isolation, and construct a valid nested OR filter only when matches exist.
Early implementations used static cache keys, which meant Vendor A's fleet results were being served to Vendor B from the Next.js cache.
Fix: Serialise all scope parameters, vendorId, page number, and active filter state, directly into the cache key, guaranteeing per-tenant isolation with automatic invalidation on data mutation.
Authenticated pages suffered 1-3 second redirect flickers. Client-side auth checks queried Supabase on every page load before deciding whether to redirect, creating a visible blank flash.
Fix: Decode the session JWT cookie directly in Next.js Middleware at the edge. Role resolution now happens in under 50ms, before the page begins rendering, zero flicker.
Resetting search parameters via URL correctly cleared the query string, but uncontrolled input fields retained their previous values, the text boxes wouldn't visually clear because React never re-mounted the component.
Fix: A reactive
key
prop tied to the search value forces React to destroy and recreate
the input node on every reset, zero state management overhead.
From sub-35ms inventory loads to a booking flow customers complete in under three minutes.
The team didn't just build what we asked for. They designed the billing transparency we couldn't articulate ourselves. Customers now know exactly what they're paying before they tap confirm. That trust has changed everything.
Whether you need a website, an online store, a web application, or a custom solution, we'll help you find the right approach and build it from the ground up.