
India is the world's biggest plastic polluter. I started with the water bottle.
How I built Quench on AWS, so events can swap plastic bottles for refill stations that don't run dry.
Series: BharatBuildsTour - by Kavyan (1 article)
- 1India is the world's biggest plastic polluter. I started with the water bottle. This article
Look at almost any photo of plastic pollution. A beach, a river, a landfill, a drain after the rain. Somewhere in it, there's a PET water bottle. It's so common we've stopped seeing it.

Bottles are hard to avoid for a simple reason. How else do you give someone a small amount of water cheaply? Metal or thick plastic would cost too much, so bottles are made as thin as possible. And cheap, thin plastic cracks and breaks apart into microplastics.
The plastic is already in the water. One 2024 study counted about 240,000 plastic particles in a liter of bottled water , most of them too small to see. It starts before you open the cap. In a study from Thailand , sealed bottles shed more plastic the longer they were stored. Bottles kept outdoors had about three times as much as bottles kept in a cool, dark room. Think of the crates stacked in the sun outside an event.
I'm not against bottles. If you're traveling, a bottle makes sense. But if you'll drink it now and throw it away, why use one at all?
India's number one problem
Here's a number that stopped me. India emits more plastic pollution than any other country. Not second. First.
A 2024 study in Nature put it at 9.3 million tonnes a year . That's nearly a fifth of the world's total. China has approximately the same amount of people as us, and emits about three times less.


It's not that we use far more plastic per person. It's what happens after. A lot of our plastic is never collected, or it's burned in the open. China built incinerators and proper landfills; we haven't caught up. So for us, the best plastic is the plastic we never use.
Why events still hand out bottles
Think about the last college fest, conference, company meeting or marathon you went to. Someone probably handed you a small plastic bottle.
I've seen this again and again. At events, at big organisation meetings, even in the pictures from huge gatherings like marathons and melas: rows of small 250 ml plastic bottles. People take a few sips, walk away, and the bottle becomes rubbish. Nobody wanted to carry water; they just wanted a drink right then. The bottles weren't there for the guests. They were there for the organisers, to spare them the nightmare of glasses, refills, and people to keep track of who refills what, and when.
The numbers add up fast. The Delhi Half Marathon in 2018 went through about 60,000 bottles in one morning. Even an ordinary college fest adds up:
One college fest: 2,000 people, 3 hours | |
|---|---|
Water they drink | 1,500 to 3,000 litres |
As 500 ml bottles | 3,000 to 6,000 bottles |
PET plastic | about 30 to 78 kg, in one afternoon |
(Same formula Quench uses: 0.25 to 0.5 litres per person per hour; 10 to 13 g of plastic per bottle.)
So why do organisers still choose bottles? Not because they're cheap. Because water is a logistics problem. With refill stations, someone has to answer these questions all day:
- How many 20-litre jars do we order, and how many go to each station?
- Which station is about to run out?
- Who takes the next jars there, and did they actually go?
- Which volunteer stopped paying attention an hour ago?
Get it wrong and a station runs dry mid-afternoon, with a queue of thirsty people. Bottles make all of that disappear. That's why they win.

But refill can work. IIT Bombay's half marathon replaced 13,000 PET bottles with 13,000 kulhads . Maha Kumbh 2025, the largest gathering on earth, set up drinking water about every 400 metres to avoid bottled water.

The logistics are the hard part. And that's a problem software can solve.
Quench in four moments
Quench is a water control room for plastic-free events. It handles the logistics that make bottles attractive. There's no app to install: everyone uses their phone's browser. Volunteers and runners never sign in; only the organiser needs a free account to save an event.

1. Plan. The organiser answers four quick questions: how many people, when, how long, and the weather. Quench works out the 20-litre jars and cups each station needs, hour by hour.
It also tells them which cups to buy. Ordinary paper cups are lined with plastic; IIT Kharagpur found hot drinks pull about 25,000 microplastic particles per 100 ml out of that lining. So Quench suggests kulhad, bagasse (sugarcane) or paper with no plastic lining, linked to shops nearby on Google Maps. Cheapest of all: ask people to bring their own bottle.

2. Count. Each station gets a QR code on one printed sheet. The volunteer there scans it and counts the jars and cups they actually have. The organiser sees which stations haven't counted yet.
3. Send a runner before a station runs dry. Each time a volunteer changes a jar, they tap one big button: "Jar swapped". From those taps, Quench learns how fast each station drinks water and predicts when it will run dry. If that's sooner than a runner can get there, it sends one now. The runner's phone beeps: "Take 6 jars to Food court". No reply in 3 minutes? The job goes to the next runner.

Taps work without signal: they wait on the phone and send when the network is back. Volunteers and runners can switch to Hindi.
4. See the impact. After the event: litres served and plastic bottles avoided, with the formula shown.
How AWS makes it work
Why serverless fits an event
An event is a strange workload. For 364 days there's nothing to do. Then, for three hours, hundreds of taps arrive, runners have to be chased, and a dry station is a failure people notice.
So nothing in Quench runs, or costs, between events. At the peak, everything scales on its own. It all runs in the Mumbai region, from one AWS SAM template:
sam deploy builds it, sam delete removes it.
Each service has one job:
What we needed | AWS service | How |
|---|---|---|
Website and API on one https address | CloudFront, S3, API Gateway | The site comes from a private S3 bucket; /api goes to API Gateway |
No tap lost in a rush | SQS, dead-letter queue, CloudWatch alarm | Taps are queued at once and saved in batches. One that fails 3 times is kept and raises an alarm |
Spot stations about to run dry, even when nobody taps | EventBridge Scheduler + Lambda | Every 2 minutes, it predicts each station's run-dry time |
Send a runner, and someone else if they ignore it | Step Functions | One run per job; waiting for the runner costs nothing |
Store everything | DynamoDB, on demand | One table, one partition per event |
Organiser accounts, without a login server | Amazon Cognito | Email + password with an emailed code; the API checks the token itself |
Old data cleans itself up | DynamoDB TTL | Everything is deleted 90 days after the event (a day for the demo account) |
Deep dive 1: every tap counted exactly once
Here's what happens to one tap:

The key is step 6. Every tap gets a random id on the phone. Saving the tap and updating the station happen in one DynamoDB transaction, and it only succeeds if that id is new. Phones can retry and SQS can deliver twice; the tap still counts once.
From
src/lib/taps.js (trimmed):js
await db.send(new TransactWriteCommand({TransactItems: [{ Put: { // store the tap...TableName: TABLE,Item: { PK: `EVT#${eventId}`, SK: `TAP#${tap.uuid}`, type: 'tap', tapType: tap.type, tappedAt: tap.tappedAt },ConditionExpression: 'attribute_not_exists(SK)', // ...only if this id is new} },{ Update: { // and count it on the station, in the same transactionTableName: TABLE,Key: { PK: `EVT#${eventId}`, SK: `STN#${stationId}` },ConditionExpression: 'attribute_exists(SK)',UpdateExpression: 'ADD swapCount :one',ExpressionAttributeValues: { ':one': 1 },} },],}));// A retried tap fails the first condition: DynamoDB cancels the whole transaction, so nothing is counted twice.
One thing SQS taught me: a standard queue doesn't keep order. A late, older tap must not overwrite newer data. So a station's "last tap" time is only written if it moves forward (
lastTapAt \< :at ). A code review caught this one before any event did.Deep dive 2: waiting for a runner costs nothing
This is my favourite piece of AWS in the project:

Step Functions has a pattern called "wait for a task token". The state machine pauses, hands out a token, and does nothing until the token comes back. In Quench, the runner's "On my way" tap sends it back. If 3 minutes pass, the state machine times out on its own and picks the next runner. No polling, no timers in my code.
The "wait for the runner" step in the SAM template (trimmed):
YAML
WaitForOnMyWay:Type: TaskResource: arn:aws:states:::lambda:invoke.waitForTaskToken # pause until the token comes backTimeoutSecondsPath: $.ackTimeoutSeconds # 180 sParameters:FunctionName: ${DispatchFunctionArn}Payload:action: wait_ackjobId.$: $.jobIdtoken.$: $$.Task.Token # saved on the job; the runner's tap sends it backCatch:- ErrorEquals: [States.Timeout] # nobody tapped in time...Next: ReassignRunner # ...so give the job to someone elseNext: WaitForDelivered
The token never reaches a phone. The runner's tap goes to my API, which checks the runner's link and calls
SendTaskSuccess on the server.Security: accounts only where it matters
Volunteers won't create accounts in the middle of an event, so they never do. But anything that stores data and costs money needs one:
- Anyone can plan and look around. Saving an event needs a free account in Amazon Cognito (email, password, a code by email).
- The API checks the token itself: signature against the user pool's public keys, issuer, audience and expiry, with no SDK. The user id comes only from the verified token, never from the browser.
- Guardrails on cost: an account runs at most 20 events at once. The public demo account for judges gets 5, and its events are deleted after a day.
- Every QR code carries its own secret. A station's id alone gives you nothing.
- Each Lambda can only touch the one table, queue or state machine it needs.
- Old data deletes itself (DynamoDB TTL, 90 days after an event).
- The API is throttled too:
YAML
DefaultRouteSettings:ThrottlingRateLimit: 50 # requests per second; our tested peak is about 17ThrottlingBurstLimit: 100
What it cost, and the bug that taught me to check the bill
Quench's AWS usage for the first ten days of October was about $0.71, covered by credits. Almost all of it came from one bug. Test events with no runners kept re-opening runner jobs in a loop. That was about 28,000 Step Functions transitions in a day. The free tier is 4,000 a month. Now a job only starts when a runner is actually free. Waiting is free; looping isn't.
How I know it works
- 70 unit tests and 45 integration tests (on DynamoDB Local, using the open-source SAM CLI), including forged and expired sign-in tokens.
- 32 browser scenarios with 117 checks, run against the live AWS site too, signing in through real Cognito.
- A load test on AWS: 500 taps in 30 seconds, none lost.
Questions people ask
"Why not just a WhatsApp group?" A group can work if everyone is disciplined. But nobody plans the order, nobody checks the stock before the gates open, and a silent station looks just like a fine one. Quench does all three, and passes an ignored job to the next runner.
"Paper cups are waste too." Yes. The goal is to cut harmful waste. A kulhad or bagasse cup goes back to the earth; a PET bottle doesn't.
"Doesn't India recycle most of its PET?" You'll see 70 to 90%. That figure comes from an industry-funded study with 2015-16 data , and the 90% counts bottles reused at home. Bottles dropped at an event become litter first. And the microplastics are in the water either way.
What it doesn't solve
Quench only works if volunteers tap. I simulated a 3-hour evening, 20 times, against a WhatsApp group. When volunteers record 90% of jar changes, stations were dry for 55 minutes in total with a typical group, and 4 with Quench. Even a very disciplined group (every last jar reported, read in 2 minutes) had 9 against Quench's 3. But if only 70% of taps happen, the disciplined group catches up: 9 against 10. These are simulation results, not data from a real event.
The simulation also caught a mistake in my own app. Quench used to top a station up to about an hour of water, often just one or two jars. At the peak, runners spent their time carrying half-empty trolleys, and Quench lost to the WhatsApp group in every run. Runner time is the scarce thing, not jars. Now each delivery covers about two hours at that station's rate, 2 to 6 jars, and less near closing time. It also stopped trusting its own count of jars left, because one missed tap makes that count too high.
Quench hasn't run at a real event yet. That's next: a college fest, then a city run. Before real users, I'll move the role rules into a Cedar policy file and add per-user rate limits.
Try it
- Live: quench.kavyan.dev . Set up a pretend event, then open a station's QR link on your phone.
Organising a fest, a meetup or a run this year? Try it without plastic bottles. The water is the easy part. Quench handles the rest.
Built for Environmental Hacks (WeMakeDevs × AWS), Waste and Energy track. I used Claude Code (Anthropic) to help write code, tests and research; the idea, the decisions and the opinions are mine.
Photos from Wikimedia Commons are resized and cropped to fit; authors and licences are under each photo. Diagrams and screenshots are mine.
Series: BharatBuildsTour - by Kavyan (1 article)
- 1India is the world's biggest plastic polluter. I started with the water bottle. This article
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article