AWS Builder Center

Warning Delhi's flooded underpasses hours early, with a serverless app on AWS

How our team built a serverless early-warning system that scores 69 Delhi flood hotspots every hour from the rain forecast, sends one-click Hindi/English alerts to officials, and replays real storms to prove how much warning it gives.

Delhi floods in the same places every year

On 28 June 2024, Safdarjung recorded 228 mm of rain in 24 hours, Delhi's highest June total since 1936. By morning the Minto Bridge underpass was under water and the Pragati Maidan tunnel was shut.
What struck our team is that none of this was a surprise. Delhi floods at the same few dozen places every monsoon: underpasses, low junctions and colonies at the bottom of a drain. The city already knows where they are. What it doesn't get is a few hours' warning of which of them will flood tonight.
Today the flood control room watches the sky and the complaint line, and pumps move once a road is already flooded. A rain forecast for "Delhi" doesn't tell a traffic inspector which underpass to close. So for the WeMakeDevs x AWS Environmental Hacks (Heat and Water track), we set out to turn the forecast into a ranked, spot-by-spot warning an official can act on in one click.
A road bridge over a flooded area in New Delhi, with water up to the top of a fence


What we built

Every hour, the system scores 69 known Delhi hotspots with one simple formula:
Risk = RainTrigger × Susceptibility
  • RainTrigger comes from the heaviest rain forecast for that spot in the next 6 hours, boosted if the last 24 hours were already wet.
  • Susceptibility is how flood-prone the spot is: how many recent monsoons it flooded in, how far it sits below its surroundings, and how much land drains into it.
Each spot gets a level (LOW, WATCH, HIGH or CRITICAL), and the city gets a status from Standby up to Severe.
On top of that score sit four things an official actually uses:
  1. A dashboard with a live risk map, a ranked list in English or Hindi, and a "why is this spot at risk?" panel that shows the four factors behind every score.
  1. A pump planner. Tell it how many pumps you have and it says where each one goes first, and which high-risk spots are left without one.
  1. One-click alerts. A signed-in official sends a warning in Hindi and English, with what to avoid and the diversion to take, to the Telegram groups traffic police and residents' associations already use. A repeat within 30 minutes is caught and not sent twice.
  1. Honest replays. The replay player re-runs past storms hour by hour, using only the forecasts that existed at that hour. It shows when we would have flagged each spot and when the first flood report actually appeared.
Risk map of Delhi at 23:30 on 27 June 2024, with ten hotspots marked HIGH in orange


How it runs on AWS

Everything is serverless in the Mumbai region (ap-south-1), split into two stacks built and deployed with the open-source AWS SAM CLI. Nothing runs on a server we manage, so it costs almost nothing while idle.
Architecture diagram: EventBridge Scheduler, Lambda, DynamoDB, S3, API Gateway, Cognito, Amplify and Parameter Store in two SAM stacks

The core stack (data and scoring):
  • Amazon EventBridge Scheduler wakes the scorer at two minutes past every hour.
  • AWS Lambda (Python 3.12 on arm64) pulls the Open-Meteo forecast and scores every hotspot. The risk model ships as a Lambda layer, so the exact same code runs the replays and the unit tests.
  • Amazon DynamoDB stores the hotspots and each hour's snapshot, which expires on its own after 30 days (TTL).
  • Amazon S3 holds the replay files for past storms.
  • Amazon API Gateway (HTTP API) serves the map data to the dashboard, read-only.
  • AWS Open Data: the Copernicus 30 m elevation model comes straight from the copernicus-dem-30m bucket. We used it to work out how low each spot sits and how much land drains into it.
The ops stack (people and alerts):
  • Amazon Cognito signs officials in. There's no self sign-up; an admin adds each official.
  • A second API Gateway HTTP API checks the Cognito login on every route, so a request without one gets a 401 and never reaches our code.
  • Lambda sends the bilingual Telegram alert and logs it in DynamoDB, which also blocks a repeat within 30 minutes.
  • AWS Systems Manager Parameter Store keeps the Telegram bot token encrypted, out of the code and out of the repo.
  • AWS Amplify Hosting serves the React dashboard over HTTPS.
Around it: Amazon CloudWatch Logs gets one line per scoring run, IAM Identity Center gave each teammate their own sign-in without shared keys, and an AWS Budgets alert at $20 kept us calm as students.

My part: building it as a near-beginner

I'll be honest: when this hackathon started, I had never deployed anything to AWS. I didn't know what CORS was, what a CloudFormation stack did, or why a browser would refuse to talk to an API. My job on the team was everything an official touches: the dashboard, sign-in, alerts, hosting and the demo.
We built the code with Claude Code as our AI coding assistant, and we split the work into three agent sessions with separate jobs, the way a small team would:
  • Agent C wrote a shared contract first: the risk formulas, every JSON shape, both APIs and the deploy names, plus sample data that obeys the formulas. Then it froze the contract.
  • Agent A (run by my teammate Shashwat) built the data, the model and the core stack.
  • Agent B (run by me) built the dashboard and the ops stack against the sample data, before Agent A's API even existed. When I needed something the contract didn't give me, I wrote a request in a handoff file and kept going instead of waiting.
  • At the end I ran Agent C again myself to integrate: it merged both branches with no conflicts, settled the change requests and ran the end-to-end checks.
The agents wrote the code, but the decisions stayed with us. Every AWS change was shown to me as a CloudFormation change set (a list of exactly what would be created, modified or replaced) and nothing ran until I approved it. I created the Telegram bot and groups myself, and I put the bot token straight into Parameter Store so it never passed through the AI or the repo. I signed in to the live site and sent a real alert to confirm it all worked.
The moment it clicked for me was seeing the first bilingual alert land on my phone from a dashboard I had deployed a few hours earlier.

Five things I learned the hard way

  1. "Failed to fetch" usually means CORS. Our hosted dashboard signed in fine but showed no data. The API was working; the browser was blocking it because the API only allowed requests from localhost. The fix was adding the Amplify address to the API Gateway CORS settings on both stacks. Nothing in the logs pointed to it, only the browser's console.
  1. Always read the change set. To change one setting, we first tried re-using the previous template. The change set showed our CORS list becoming "localhost, localhost": the SAM transform had kept the old value. We deleted it unrun and built the change from the deployed template instead.
  1. rate(1 hour) is not "on the hour". EventBridge Scheduler counts from when the schedule was created, so our snapshots were made at :47 but stamped with the start of the hour, and looked up to 107 minutes old. Switching to cron(2 * * * ? *) fixed it. That runs at two minutes past each hour in UTC, which is :32 in India.
  1. Windows adds invisible characters. The AWS CLI on Windows ends its output with a hidden carriage return, which broke the upload address in our Amplify deploy script and left two deployments stuck. Line endings also made SAM think unchanged code had changed.
  1. Secrets never go in code, chats or files. The bot token lives only in Parameter Store as a SecureString, and the alert Lambda's IAM role can read only its own parameters. Even our AI assistant never saw it.

Did it work? Including where it didn't

We replayed the 28 June 2024 storm using only the forecasts issued at the time. The city moved to Rain watch at 21:30 the night before. At 23:30, about three and a half hours before the heavy rain began, ten spots turned HIGH at once, led by the Pragati Maidan tunnel.
  • 14 spots were reported flooded.
  • 3 were flagged HIGH before their first report: Pragati Maidan tunnel, ITO and Tilak Bridge.
  • 10.6 hours was the median warning for those three.
  • 7 spots were flagged but we found no report for them.
Those numbers are upper bounds. "First report" means the first news article naming the spot, because we couldn't find timestamped official posts. And a flag with no report isn't proof of a false alarm.
We also kept a miss in our results on purpose. On 25 August 2026, about 72 mm fell in under two hours, but no forecast showed more than 4.1 mm an hour. The forecasts missed that cloudburst entirely, and so did we. We could have tuned it away; we chose to show it, because a warning system people trust has to be honest about what it can't see.

What's next

  • Higher-resolution or radar-based rain nowcasts, to catch short bursts like 25 August.
  • A post-rain flood log where officials mark which spots really flooded, so the scores get better every season.
  • Alerts routed per ward to real department groups, and longer sign-in sessions for a long monsoon night.

The team and the links

Built by Avika Thapliyal (team lead and problem research), Shashwat Tripathi (data, risk model and core backend), Yashika Verma (demo video) and me, Vatsal Gupta (dashboard, alerts, hosting and integration).
Code written with Claude Code; I also used Claude to help draft this post. Data: Copernicus GLO-30 DEM (ESA / European Union, via AWS Open Data), © OpenStreetMap contributors, Open-Meteo (ECMWF IFS). Flood photo: Shantum Singh / Pexels.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article