
How we built SaansSafe: a serverless AWS agent that tells Delhi schools whether kids can play outside
On bad-air days, principals have to decide about outdoor assembly and sports with no clear data. Here's how we turned live air quality into a school-day decision and a bilingual parent notice with AWS Lambda, EventBridge, DynamoDB, SNS and the Strands Agents SDK.
Every winter morning in Delhi, a school principal makes a call that affects hundreds of children's lungs: do we hold assembly outside today?
On the worst days the decision is made for them. Severe air triggers official closures under GRAP (Delhi-NCR's Graded Response Action Plan). But most bad-air days aren't "Severe". They're Moderate, Poor or Very Poor, and on those days every principal is on their own. They have a number on a phone app, a US-style AQI that doesn't match India's scale, and a WhatsApp group full of worried parents.
Over the Environmental Hacks weekend, our team (Saksham Bansal, Ronit Ghosh, Ujjwal Kumar and Rahul Agarwal from IIIT Delhi) built SaansSafe (saans means "breath" in Hindi). It turns live air-quality data into the decision a school actually needs to make, plus a parent notice in English and Hindi. This post covers how we built it on AWS, the design choice we're proudest of, and everything that fought back.
What it does
You type a city or locality and your school hours. SaansSafe then:
- Calculates India's official CPCB AQI from live and forecast PM2.5 and PM10.
- Gives a plain-language decision, like "Shorten outdoor time", with one line of reason.
- Scores every school hour so the safe outdoor window is obvious.
- Shows a 7-day outlook so teachers can plan indoor PE days ahead.
- Writes a parent notice in English and Hindi that you can copy, share on WhatsApp, or have read aloud.
- Gives district officials a Districts view that ranks several areas at once.
- Sends daily 7:00 AM alerts to subscribed schools, scheduled on AWS.
Architecture
Plain text
Browser ──▶ Amazon API Gateway (HTTP API) ──▶ AWS Lambda (FastAPI via Mangum) ──▶ Open-Meteo APIs│Amazon EventBridge (07:00 IST) ──▶ AWS Lambda (alert job) ──▶ Amazon DynamoDB (subscribed schools)└──────▶ Amazon SNS (morning alert)
Everything is defined in one AWS SAM template and deployed to ap-south-1 (Mumbai) with
sam deploy . It's fully serverless and pay-per-use, so a demo-sized app runs within the free tier.Service | What it does in SaansSafe |
|---|---|
AWS Lambda | Runs the web app and API, plus the daily alert job |
Amazon API Gateway | Public HTTPS endpoint |
Amazon EventBridge | Triggers the alert job every day at 7:00 AM IST |
Amazon DynamoDB | Stores subscribed schools (on-demand billing) |
Amazon SNS | Delivers the morning alerts |
Strands Agents SDK | Open-source agent framework that wraps our planning tools |
AWS SAM | Infrastructure as code for the whole stack |
The design choice: rules decide, the model explains
The first question we asked was where should AI sit in this?
An LLM shouldn't decide whether 400 children go outside. If a model produces a different answer each time, or makes up a threshold, a principal can't trust it. So we split the system in two:
- A deterministic core that calculates the CPCB AQI and the school plan. It's the same input, same output, every time, and it's unit-tested.
- An agent layer built with the Strands Agents SDK that calls those same functions as tools and can explain the result conversationally.
Turning plain Python functions into agent tools with Strands took a decorator:
from strands import tool
from . import core
@tool
def compute_cpcb_aqi(pm2_5: float, pm10: float) -> dict:
"""Compute India's CPCB National AQI and category from PM2.5 and PM10 concentrations."""
return core.compute_cpcb_aqi(pm2_5, pm10)
@tool
def plan_school_day(place: str, school_start_hour: int = 8, school_end_hour: int = 14) -> dict:
"""Fetch live data for a place and return the school action plan."""
...
We registered six tools:
geocode , get_air_quality , get_weather , compute_cpcb_aqi , plan_school_day and draft_parent_notice . The model provider can be configured (Amazon Bedrock, or an OpenAI-compatible or Ollama endpoint). The part we like most is that the app works fully without any model key. If there's no key, or the model call fails, the same tools run directly and the output is filled into templates. A model error can never break a principal's morning. In fact, the deployed demo runs in this deterministic mode.
Getting India's AQI right
Most weather and air APIs report the US EPA AQI, but India uses the CPCB National AQI, which has different breakpoints and categories (Good, Satisfactory, Moderate, Poor, Very Poor, Severe). Showing an Indian principal a US number on an Indian colour scale is quietly wrong.
So we implemented the CPCB method ourselves. Each pollutant gets a sub-index by linear interpolation within its breakpoint band, and the AQI is the maximum of the sub-indices:
def sub_index(pollutant: str, c: float) -> int:
b = BP[pollutant]
for i in range(6):
lo_c, hi_c = b[i], b[i + 1]
if c <= hi_c or i == 5:
lo_i, hi_i = CATS[i][1], CATS[i][2]
c = min(c, hi_c)
return round(lo_i + (hi_i - lo_i) * (c - lo_c) / (hi_c - lo_c))
return 500
def compute_cpcb_aqi(pm2_5: float, pm10: float) -> dict:
s25, s10 = sub_index("pm2_5", pm2_5), sub_index("pm10", pm10)
aqi = max(s25, s10)
...
We unit-tested the band edges, because off-by-one bugs at a boundary are exactly how a "Poor" day gets labelled "Moderate".
Morning alerts with EventBridge, Lambda, DynamoDB and SNS
A principal shouldn't have to remember to open an app at 6:45 AM. So schools can subscribe, and every morning:
- An Amazon EventBridge rule fires at 7:00 AM IST.
- A Lambda function reads subscribed schools from DynamoDB.
- It builds each school's plan and bilingual notice.
- It publishes the alert through Amazon SNS.
The schedule in the SAM template looks like this:
DailyAlertsFunction:
Type: AWS::Serverless::Function
Properties:
Handler: alerts_handler.handler
Timeout: 300
Events:
Daily7amIST:
Type: Schedule
Properties:
Schedule: cron(30 1 * * ? *) # 01:30 UTC = 07:00 IST
We also made storage and delivery small, swappable classes. Locally they're a JSON file and an outbox; on AWS they're DynamoDB and SNS. That means the "Preview tomorrow's 7 AM alert" button in the UI runs exactly the same job the scheduled Lambda runs.
What fought back
- Cron is in UTC. EventBridge cron expressions are UTC, so 7:00 AM IST becomes
cron(30 1 * * ? *). It's very easy to be off by 5.5 hours.
- FastAPI on Lambda. We used Mangum to run our app on Lambda. Our first package was bloated by agent and LLM dependencies, so we built a slim Lambda package with just what the web app needs.
- Indian place names are ambiguous. "Dwarka" comes back as the city in Gujarat before the Delhi neighbourhood. We rank Indian results first and use the state or area typed after a comma.
- Model data vs monitoring stations. The forecast data is a gridded model (CAMS), not CPCB ground monitors, and modelled PM10 can spike during dust events. We label hourly values as indicative and document it honestly.
- Hindi. We use templates for the Hindi notice instead of generating it, so the wording is predictable and can be reviewed. For the Listen button, we use the browser's Web Speech API with an
hi-INvoice when the device has one. Voice availability varies by device, so the app shows a hint when there isn't one.
What we learned
- Serverless removes friction. Two Lambdas, an HTTP API, a schedule, a table and a topic, all from one SAM template and one deploy command, with IAM roles included.
- Agents are better as an interface than as a judge. Strands made it easy to give an agent real tools. Keeping the decision deterministic is what makes it trustworthy.
- A calm, plain-language decision helps more than a raw number. "Shorten outdoor time; the safe window is 8 to 11" is something a principal can act on. "AQI 181" isn't.
What's next
- Use CPCB ground-station data wherever it's available.
- Per-school SMS and WhatsApp delivery (we already collect numbers).
- Per-class PE timetables, so the plan can say which periods to move indoors.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article