Inspiration
Maharashtra has world-class adventure terrain - the Sahyadri forts, Kundalika's white water, Kamshet's paragliding cliffs, the Konkan coast. But booking any of it is broken. You hunt through WhatsApp groups, message unverified guides, send a UPI deposit on faith, and still don't know if weather will cancel the trip. The supply is incredible; the trust layer doesn't exist.
We set out to build that trust layer - a real, two-sided marketplace where travelers discover and book with confidence, and local operators get the tools to run a real business. Not a catalog. A product you could actually launch.
What it does
Bhraman connects weekend adventurers with verified local operators across Maharashtra.
- Discover 73 real experiences across 50 destinations - trekking, rafting, camping, paragliding, caving, fort exploration - filterable by category, difficulty, budget, city, and date.
- Plan with AI - describe a trip in plain language ("first-time trek, scared of heights, scenic and safe") and Claude returns ranked, real, bookable listings with reasoning for why each fits. It only ever recommends listings that exist in our DB.
- Live trust signals - every listing shows a real-time weather/safety signal for its exact coordinates, verified-operator badges, response times, completed-trip counts, and reviews tied to real bookings.
- Book & pay - a clean multi-step flow with Razorpay (UPI, cards, netbanking), confirmed server-side via webhook, with atomic seat-claiming so slots can't oversell.
- Operator portal - operators manage listings, availability, bookings, and payouts, with a transparent 12% commission split. The full marketplace, both sides.
How we built it
Architecture. Next.js 14 App Router with React Server Components for fast, SEO-friendly discovery and detail pages; client components are pushed to the leaf level (booking panel, filters, planner chat) to ship minimal JS. Data lives in PostgreSQL (Neon) accessed through Prisma 6. Auth is Clerk, running separate traveler and operator portals. Payments are Razorpay. Transactional email is Resend. The AI planner is Anthropic's Claude. Deployed on Vercel with the database co-located in-region to kill round-trip latency.
The AI planner - retrieval, not generation. The hard problem with an LLM trip
planner is hallucination: a model will happily invent a trek that doesn't exist, and a
judge clicking a fake recommendation kills the demo. We solved this with a strict
tool-calling contract. Claude is given a single searchListings tool whose input schema
is constrained to our real filter dimensions (category, difficulty, price, city, date).
The model parses the user's natural-language intent into structured filters, our server
runs a real Prisma query, and Claude explains the fit of the rows that come back -
referencing them by ID. The UI renders bookable cards from those exact rows, never from
the model's prose. Two independent safety rails (the tool can only return real DB rows;
the UI only renders real rows) mean a hallucinated listing can never become a clickable
card. The model does the reasoning; the database is the source of truth.
Booking integrity. Two users hitting the last seat simultaneously is a classic race
condition. We claim seats with an atomic, conditional SQL update - incrementing
bookedSeats only when bookedSeats + groupSize <= capacity in a single statement,
inside a Prisma transaction that also creates the booking and snapshots the price.
Read-then-write would oversell; the database enforces capacity, not our code. Held seats
on abandoned (PENDING) bookings are released by a cron job, so availability self-heals.
Payments done right. The browser never confirms a payment. The client opens Razorpay's hosted checkout against a server-created order whose amount is recomputed from the booking row (never trusting a client-sent amount). Confirmation happens in the webhook: we verify the signature on the raw body, enforce idempotency (Razorpay retries, so we guard against double-confirming and double-emailing), then mark the payment captured and the booking confirmed in a transaction, and send the Resend confirmation only after commit.
Real data, real images. This isn't lorem ipsum. We assembled a dataset of 50 real Maharashtra places across 10 districts and 8 categories - 73 listings, ~313 itinerary steps, ~1,460 weekend availability slots - seeded idempotently by slug. Each place is matched to real Wikimedia Commons photography, with hero images rotated across listings that share a place and de-duplicated galleries, so the whole catalog looks and feels real.
Authorization, not just authentication. Clerk tells us who a user is; it doesn't
tell us what they're allowed to touch. Every operator mutation re-checks ownership
server-side (requireSessionOperator + per-resource ownership guards), so an operator
can never edit another's listing by changing an ID - verified across the whole operator API.
Challenges we ran into
- Stopping AI hallucination without losing the magic. Early versions invented plausible-but-fake treks. The tool-calling-plus-render-real-rows architecture was the fix - it took several iterations to get the contract tight enough that the model reasons freely but can only ever surface real, bookable listings.
- The double-booking race. Our first booking code read availability then wrote it - fine in testing, broken under concurrency. Moving to a single atomic conditional update was the unlock.
- Operator auth loops. Building two Clerk portals (traveler + operator) on one app
created redirect loops for signed-in users who weren't operators. We added an explicit
/operator/unauthorizedstate and a sign-up → complete → sign-in flow to break the loop. - Sourcing real, license-clean imagery for 50 specific places - mapping each place to genuine Wikimedia photos and handling shared-place de-duplication.
Accomplishments that we're proud of
- A genuinely production-ready, deployed build - real DB, real payments, real auth, real data - not a prototype.
- An AI planner that does real retrieval work and provably never hallucinates a listing.
- A complete two-sided marketplace - both the traveler journey and the operator business tools - with a working commission model.
- The trust layer: live weather/safety signals per location, which we haven't seen on any comparable platform.
What we learned
- For LLM features, constrain the model to your data and render from the data, not the text - that's the difference between a demo gimmick and a trustworthy product.
- Money and seats are transactions, never read-then-write. The database is the arbiter of truth.
- Authentication ≠ authorization - every mutation needs a server-side ownership check.
- Real data and real imagery change everything about how a product feels to a user and a judge.
What's next for Bhraman
- Razorpay Route for automated operator payouts at scale (live KYC).
- Group/split payments and waitlists for full slots.
- A review pipeline gated on completed bookings, with photo reviews.
- Operator analytics: demand heatmaps by region and season.
- Expansion beyond Maharashtra to Himachal, Uttarakhand, and the Western Ghats belt.
Built With
- anthropic-claude
- clerk
- framer-motion
- neon
- nextjs
- node.js
- open-meteo
- postgresql
- prisma
- razorpay
- react
- resend
- tailwindcss
- typescript
- vercel
- wikimedia-commons


Log in or sign up for Devpost to join the conversation.