Book a football turf in Sylhet without calling anyone.
Chapters8 · 1:00
To book a turf, you call the manager. Often, nobody picks up. And when they do, it’s a notebook: two teams, one hour. Zenvy: Skip the call. Book the turf.
See which hours are open. Turfs across Sylhet, with live slots and both prices up front.
Take the hour. Pay a small advance: ৳200 an hour by default, by bKash, Nagad, Rocket, Upay or card. Your code comes by SMS. Show it at the gate.
Two teams can’t get the same hour. The database itself refuses double bookings: fire 20 at one hour, and exactly one wins.
Retire the notebook and the Facebook inbox. Bookings ping your phone and money lands in your bKash. No signal? It still works. Listing costs nothing.
bKash, Nagad, Rocket, Upay, card. Pay the advance the way you already pay.
মাঠ বুক করুন। Book the pitch. দল নিয়ে আসুন। Bring the team. Bangla by default, English at a tap.
Skip the call. Book the turf. zenvy.com.bd, made in Sylhet.
Book a football turf in Sylhet without calling anyone.
At a glance
- 01The problem
- In Sylhet, booking a turf means calling a manager who often doesn't answer.
- 02What I built
- Online booking with a small advance by bKash or card, and an owner app that works offline.
- 03The result
- Live in Bangla and English, and the database makes double bookings impossible.
Built with
Next.js · next-intl · Express · PostgreSQL · Prisma · PayStation
The goal, in the live site's own words
“Live availability at turfs across the country. See which hours are open, take one, and get confirmed on the spot.”
The problem
Football in Sylhet happens on small artificial-grass turfs, rented by the hour. To book one, you call the manager. Often nobody picks up, and when they do, they're checking a notebook while another team is asking about the same hour.
That's the whole problem: you can't see what's free, and nobody can promise an hour is yours. Players waste evenings, and grounds lose money on empty hours they didn't know they had.
Who it's for
Player · illustrative
Rafi, 22
Books for his team of ten on Friday nights. Wants to see a free hour, pay the advance by bKash and get a code he can show at the gate.
Ground manager · illustrative
The person at the desk
Runs the ground from a phone all day, takes cash from walk-ins, and isn't a computer person. Needs big buttons, plain words, and a phone that still works when the signal drops.
Try it
Find a free hour on the real site, live in Sylhet.
Live Find a turf on Zenvy zenvy.com.bdWhat I built
Two apps on one platform: a booking site for players, and a phone-first app for the people who run the grounds.
How it works
- Find a free hourSee every open hour at every ground, day and night prices side by side.
- Pay a small advance৳200 an hour by default (each ground sets its own), by bKash, Nagad, Rocket, Upay or card.
- Get your code by SMSShow it at the gate. The rest is paid at the ground.
Decisions I made
Decision Live Let the database refuse double bookings
- Options
- Check in the app, lock rows, or put a rule in the database itself.
- Why
- A Postgres exclusion constraint refuses any two bookings whose times overlap. It holds on every code path, even for overlapping time ranges where there's no single row to lock. A test fires 20 claims at the same hour at once, and exactly one wins.
- Cost
- Harder migrations, and database errors have to be turned into friendly messages.
For engineersTechnical
EXCLUDE USING gist (fieldId WITH =, tstzrange(startAt, endAt, '[)') WITH &&), with SQLSTATE23P01mapped to HTTP 409.- Full and half pitches share inventory through a parent field, so booking the full pitch blocks both halves.
Decision Live Don't trust the payment redirect
- Why
- The gateway's redirect back to the site isn't signed, so a booking is confirmed only after a server-to-server status check, and webhooks are verified with an HMAC signature.
- Cost
- One extra round trip at checkout.
For engineersTechnical
- Idempotency keys on payment rows, so a retried webhook can't pay twice. Late payments are recorded first, then settled against the constraint.
Decision Live An owner app that works without signal
- Why
- Cash taken at the ground goes into an outbox on the phone and syncs when the connection comes back, without being counted twice.
- Cost
- Risky actions, like blocking an hour, are refused while offline.
For engineersTechnical
- An IndexedDB outbox with a client request ID for each write, so the server ignores replays. A month of data is pre-loaded for offline use.
Decision Live Designed for managers who may not read easily
- Why
- Amounts and names can be read aloud in Bangla at the tap of a button. The day is picked by part (morning, evening, night) instead of a 24-hour list, payment types are icons, and every target is at least 44 px.
Decision Live Bangla first
- Why
- Bangla is the default, English is at /en. English SMS writes 'Tk' instead of ৳, because one non-GSM character turns a 160-character message into a 70-character one and roughly doubles the cost.
Decision Live A deposit people can remember
- Why
- The advance used to be 20 % of the price, which produced figures nobody chose, like ৳260 or ৳440. Now it's a flat amount per hour, the same number in the SMS, on the receipt and at the gate.
- Cost
- At cheap grounds, a flat per-hour deposit is a bigger share of the price.
Decision Live Zero commission at launch
- Why
- It was 5 %. The first grounds are taking a risk on an unproven platform, and charging them is the wrong trade while supply is the constraint. Commission is saved on each booking, so changing it later never rewrites money already earned.
- Cost
- No platform revenue from bookings until demand justifies a cut.
For engineers: the rest of the stackTechnical
- Asia/Dhaka
timestamptzeverywhere, and operating days that run past midnight. - Additive migrations deploy on their own. Destructive ones wait for a person.
- GitHub Actions deploys over SSH, checks
/health, and rolls back on failure. - The payout ledger query went from 426 ms to 54 ms on a synthetic 73,000-booking benchmark.
The redesign
The new design, rolling out at zenvy.silverfish.dev, puts search and free hours first.
Decision New design A booking site, not a marketing page
- Why
- The first version read like a SaaS landing page. Now the first thing you see is a search box and the free hours.
Decision New design Skip the call. Book the turf.
- Why
- Longer taglines were cut as wordy. The whole promise fits in five words.
Decision New design Readable on a phone
- Why
- I darkened the text greys after finding them too light on my phone. Body text is now 6.0:1. Small meta text is 4.1:1, which I'd raise to 4.5:1 next.
Decision New design Every colour also has a word
- Why
- Free, Due, Paid and Closed are never shown by colour alone, because colour fails in glare and for colour-blind managers.
Results
What I'd do next
Fill the quiet hours with a forecast. With enough booking history, a model can predict which hours will stay empty, from the day, the time, the weather and holidays, so owners can discount them early. It's the first thing I'd build with the machine learning I'm studying.
Project receipt · Zenvy
Checked 30 Sep 2026against code and the live site
- My part
- Co-founder & CTO. Architecture, payments, the owner app [to confirm]
- Built with
- Next.js 15, Express 5, Prisma 6, PostgreSQL (Supabase), next-intl, ImageKit, Sentry
- Shipped
- Live at zenvy.com.bd. A new design is rolling out at zenvy.silverfish.dev
- Tests
- About 900, run against a real Postgres on every push
- On the site
- 15 turfs listed · 4 areas · 5 bookable now (zenvy.com.bd, 2026-09-30)
- Sources
- zenvy.com.bd · the production repo · the redesign's handover notes