Side project · Co-founder & design engineer · Jamshedpur, India · 2026
A venue that books itself inside WhatsApp.
Project 831 is a two-court pickleball venue I co-founded and run. I designed and built the system it runs on, from booking inside WhatsApp to a desk dashboard and a kids' programme that renews itself. I also wear the other hats: the money, partnerships, brand, marketing and the edits.
Every hat · co-founder
The software is one job of many.
I run the money, negotiate the partnerships, build the brand, market it and cut the videos. Each one shaped what the product needed to do.
Every rupee and every negotiation
Pricing, settlements and the deals behind them, from the coach's revenue split to brand partnerships.
Four brands for one tournament
I pitched and closed the 831 City Open's partners, with a deck built for each brand.
831 reads as "I love you"
The pincode is the name. A heart hidden in the 3, a terracotta palette and Dalmo, our mascot, for partner creatives.
Found on Google, run from code
SEO with structured data on project831.in, a Google review funnel, and Meta and Google ads I run myself through the API.
Instagram, edited by me
Close to 450 followers, and I edit every reel.
The engine underneath
The booking system, the website and the dashboards: the rest of this page.

831 City Open · 4–5 July 2026
A city championship, run on our own system.
Registration, payments, draws and the schedule ran on the same engine as court bookings.
Ball
Hydration
Sunscreen

Booking · real screenshots, 15 October
Six taps from “831” to a confirmed court.
No app, no website, no login. A customer types the venue's name into WhatsApp and books a court without leaving the chat.






Each version of the flow asks for less.
v4 let you pick several slots. v5 added a kit screen. v6 asks one “When?” question, moves duration to the slot screen and shows no price copy until the summary.
Meta integration · WhatsApp Cloud API + Flows
WhatsApp is the front door, so I built it like one.
The bot, the booking Flow, the confirmations and the marketing all run on Meta's WhatsApp Cloud API, wired straight to the booking engine.
Chat, buttons, a 3-screen Flow, in-chat pay.
Cloud API
Signed webhooks in; templates and messages out.
Webhook + Flow endpoint
Encrypted data exchange returns live slots and prices per screen.
Postgres
Bookings, money, flow events. Row-level security on every table.
An encrypted form inside chat
RSA-OAEP + AES-GCM on every screen. The first screen ships with its data, so it opens with no server round trip.
A Flow builder in the dashboard
Draft, preview, publish and promote Flows through the Graph API without opening Meta's tools.
Confirmations that always land
Approved templates by default, a 30-minute safety margin on the window, and a retry when Meta refuses.
Marketing can't break login
Promotions go out on a second number, so a block or report never touches the number that sends login codes.
Every abandoned booking is visible
Seven stages are logged per person; the desk can nudge a drop-off with one tap, inside the window only.
Utility and marketing kept apart
Opt-ins stored with evidence, a Stop promotions button on every campaign, and sends paused when Meta rate-limits.

Payments · Razorpay, inside WhatsApp and on the web
The data said: take the money first.
Booking was free, so skipping it was free too. Bookings with nothing paid kept getting cancelled, and some people just didn't turn up. So the engine learned to ask for money before it holds a court.
What each finding turned into
Flow confirms
The engine decides: pay in full, part pay, or pay at the venue.
Court held 15 min
A pending row blocks the slot in Postgres; nobody else can take it.
Review and pay card
Meta's checkout, Razorpay underneath. UPI or card without leaving the chat.
Checked twice
Meta's payment status, re-checked with Razorpay: captured, INR, same amount. Then the booking confirms.
One payment layer, every product
Courts, tournaments, events, Off-Screen Club memberships and enquiries all pay through the same Razorpay orders and links.
The venue sets the rules
Deposit percentage, which days need prepayment, and per-customer flags: pay first, or trusted to pay at the venue.
Reference before request
The order reference is stamped on the booking before the card is sent. If a send fails silently, the hold stays and no second link goes out.
Signed, then believed
HMAC-SHA256 on the raw body with each venue's own secret. Replays are ignored; late money is flagged to the desk, never lost.
Keys the dashboard can't read
Each venue's Razorpay keys are encrypted with AES-256-GCM and a rotating keyring. The dashboard can write them, never show them.
HDFC behind a switch
Web checkout can route to HDFC SmartGateway per venue. Built and tested with a ₹1 harness; waiting for launch.
Prepaid bookings have never been a no-show.
The desk · bookings dashboard
The desk stopped being the way in.
Self-serve bookings went from 15% to 74% of the month in four months. The desk now reads the day instead of typing it.

Off-Screen Club Juniors · kids' fitness, run with a coach
A weekly programme that runs, renews and splits itself.
Saturday and Sunday batches for children, run with an outside coach. Parents sign up on the website or at the desk; the system handles the month after that.
Who keeps what
Split after shared costs. Both sides open the same accounts page, every line needs the other side's tick, and a settled week is frozen by the database.
Three plans, one register
₹2,999 for every session in the month, ₹1,999 for any four, ₹599 for one. Memberships stay on the register until someone stops them, and attendance is the record of what was used.

Renewals chase themselves
Planned every morning at 7, sent between 9 AM and 8 PM, at most two a week per family, cancelled the moment the family pays. Each sequence can run off, for review, or automatically.

One grant opens the programme
The coach gets a collaborator role that starts with zero access. A grant on the programme opens every plan and batch inside it.
Given per person, per programme
The coach's team takes cash at the venue and records it. Prices, refunds and deletes stay with the venue.
Attendance from a link and a PIN
A signed WhatsApp link opens one programme's register for 60 days, never shows parents' numbers, and dies when the key rotates.
The coach works in chat
Enrolments and payments come in through a desk Flow and pass the same rules as the dashboard.
APIs · 12 serverless functions, 6 outside services
One engine, many doors.
WhatsApp, the website, the dashboard and the voice agent all call the same booking API. No business rule exists twice.
| Endpoint | Serves | Detail that matters |
|---|---|---|
| /api/webhook | Every WhatsApp message, status and payment | Signature checked on the raw body; duplicates claimed once |
| /api/flow | The booking Flow's screens | End-to-end encrypted; slots and prices from the live engine |
| /api/bookings | Dashboard and website bookings | Public reads and creates rate-limited per visitor |
| /api/events | Programmes, registrations, attendance, collaborators | The coach's WhatsApp entries call it in-process with his identity |
| /api/razorpay | Payment webhooks for Razorpay and HDFC | Never trusts a callback; re-reads the payment first |
| /api/admin-data | The dashboard: money, staff, messages, settlements | About 36 resources behind role and grant checks |
| /api/cron | Reminders, follow-ups, briefings, renewals | Six schedules across every venue on the platform |
| /api/v1 | The voice agent | Per-venue keys, rate limits, idempotent bookings |
Messages, templates, Flows, payments
API version pinned in code, with an alarm 90 days before Meta retires it.
Three payment rails, one settlement
Checkout, payment links and in-chat WhatsApp Pay all finish in the same code.
Built to host more than one venue
Each venue's keys are encrypted at rest with a rotating keyring; webhooks resolve the venue from the URL and the number.
Also: Postgres with deny-all row-level security, web push for the desk, Vercel cron. Tests run behind a network guard, so no test can reach a real service.
Security · built in, not added later
Locked down like it holds real money. It does.
Every request proves who it is and which venue it belongs to before it gets near the data. The browser never talks to the database at all.
Holds a key it can't read
The session sits in a cookie scripts can't touch. Requests from other sites are refused.
Checked on every request
Signed, compared in constant time, and re-checked on the server each time, so sign-out everywhere is instant.
One session, one venue
Built for many venues. A session is bound to one, and editing the URL can't cross into another.
Server only
No table access from the browser, row-level security on, backed up every day.
End-to-end encrypted booking
The booking form inside WhatsApp is encrypted end to end: RSA-OAEP key exchange, then AES-GCM on every screen.
Signed webhooks
WhatsApp and payment callbacks must carry a valid signature. A payment that doesn't match its order changes nothing.
Secrets encrypted at rest
Each venue's WhatsApp, payment and voice keys are sealed with AES-256-GCM under versioned keys that can be rotated.
Limits that fail closed
Sign-in, WhatsApp codes and resets are rate-limited across every server. If the limiter is down, it refuses. It stores hashes, never a phone or IP.
Links instead of accounts
Coaches mark attendance from a signed link plus a PIN. Rotating one secret revokes every link at once.
3,100+ tests on every change
They run with the network blocked, so no test can touch production. CI also stops real phone numbers or a venue-boundary slip from merging.
Also: a public security contact for responsible disclosure, customer images served through the server so no token reaches a page, and an offline cache that never stores private responses.
Voice agents · two attempts, both parked
We tried to answer the phone with an agent. Twice.
Plenty of customers still prefer to call, so we built a voice agent that could book. It worked; it wasn't worth keeping. What lasted is the API it forced us to build.
Our own agent on WhatsApp calling
- Meta WhatsApp calling → a Node server in Mumbai
- Sarvam AI for speech, model and voice; English, Hindi and Hinglish
- Could check slots, book, cancel and reschedule
- Interruptions handled; worst-case reply cut from 10 s to 3 s
28 calls and 12 test bookings in 72 hours. Then a review found it fragile: booking logic copied outside the app, calls held in memory, a reschedule that could lose a booking.
A voice platform, our API in charge
- Dograh as the voice layer; the app keeps every booking rule
- Narrow tool API: venue info, dates, slots, the caller's bookings, book, cancel
- Idempotency keys, confirmation sent once, caller sees only their own bookings
- 152 example conversations prepared for tuning tone
Booked end to end in testing, never with a real customer. The WhatsApp-to-phone route was never proven; voice was switched off in October.
The lesson: buy the voice, own the booking rules.
Rebuilt on coss ui · open source, runs in the browser
The same product, on Cal.com's own components.
The booking page, part payments, the 15-minute hold and the desk, rebuilt end to end with all 55 coss ui components, installed with the shadcn CLI and unmodified. Demo data only.
github.com/rkj-builds/slot-picker
React 19, TypeScript, Base UI, Tailwind v4. Headless primitives of my own for slots, recurrence and time zones. 433 tests with accessibility checks.
The Demo menu
Jump to any of 74 states: part payments, a declined card, a slot taken mid-checkout, empty and error states. Then the Primitives page and ⌘K on the desk.
How it was built · 28 March – 14 October 2026
Designed and built by me, with Claude Code.
68% of commits co-authored with Claude Code. I specify and review; the machine types.
React 19, Vite PWA, Postgres
12 serverless functions on Vercel, a separate Next.js marketing site, no component library.
Production data is never test data
3,100+ tests run offline; a mock mode answers every API call in the browser; a PII scanner blocks real phone numbers in the repo.