Inspiration
Civic issues — potholes, broken streetlights, overflowing garbage, water leaks — rarely get reported, and when they are, the process is opaque: citizens have no idea whether anyone even saw their complaint, and municipal staff drown in unstructured phone calls and paper forms. We wanted to build something that met citizens exactly where they already are (WhatsApp), used AI to do the tedious triage work automatically, and kept a hard privacy line: public accountability data should be aggregate-only — no one's individual complaint, phone number, or home address should ever be exposed, to the public or even to each other.
What it does
CivicKural (WardWatch) lets a citizen report a civic issue by sending a WhatsApp message — text, a voice note in English or Tamil, a photo, a video, or a location pin — or through a web form. An AI agent (Amazon Bedrock) classifies the issue's category, checks whether the photo evidence actually shows the problem, estimates priority, and pins the location using a four-tier fallback (GPS → geocoded address → ward centroid → manual). Deterministic rules (not the AI) handle deduplication, routing to the right department, SLA timers, and escalation — so the parts that must be predictable and auditable never depend on a model's mood. Officers get a dashboard to triage and resolve; an AI verification step compares before/after photos to confirm the fix; the citizen gets a status update in their own language and can confirm or reject the resolution. The public only ever sees aggregate numbers — totals, category breakdowns, SLA compliance, a density heatmap — never anyone's individual report.
How we built it
- Backend: FastAPI on AWS Lambda, DynamoDB (single-table design) for storage, private S3 for evidence media, Amazon Bedrock for multimodal classification and verification, Amazon Location Service for ward-biased geocoding, Amazon Transcribe for voice notes, KMS for envelope-encrypting citizen contact info.
- Orchestration: a Strands tool-choosing agent (with an AgentCore Runtime adapter) handles the parts that genuinely benefit from reasoning, while routing, SLA, deduplication, and escalation stay deterministic Python — auditable and never able to bypass privacy or lifecycle rules.
- Frontend: a statically-exported Next.js app on Amplify — separate views for officers (queue, assignment, resolution), coordinators (read-only ward overview), admins (config, system health), and citizens (submit, track, public dashboard).
- Messaging: a real WhatsApp Business Cloud API webhook, with signature verification and multi-message session assembly (a citizen's text, photo, and location sent as separate messages get stitched into one report), and AWS SNS for SMS fallback to citizens who submit via the web instead of WhatsApp.
- Auth: Cognito-backed JWT sessions for officer/admin staff, with ward- and role-based enforcement on every mutating endpoint.
Beyond the initial build, we ran a full hardening pass: fixed a public-endpoint privacy leak (individual low-count locations were visible on the public map — added k-anonymity suppression), closed an unauthenticated webhook bypass, rotated the production JWT signing secret, made escalation notifications retry-safe and idempotent, and added an internal system-health view.
Challenges we ran into
The hardest constraint was making privacy non-negotiable by construction, not by policy. Every design decision had to answer "does this leak an individual's data?" — the public dashboard only serves pre-aggregated, k-anonymity-suppressed data (we caught and fixed a bug where a single isolated report could still reveal someone's approximate location); citizen contact info is KMS-encrypted with purpose-scoped encryption contexts, never stored in plaintext; and even our new phone-OTP login only stores a SHA-256 hash of the phone number for lookups, keeping the reversible ciphertext behind a purpose-locked KMS key used only at the moment a notification actually needs to go out. WhatsApp's multi-message nature (a report often arrives as 3-4 separate messages) also meant building session assembly with proper expiry, rather than assuming one message equals one report.
Accomplishments that we're proud of
- A genuinely privacy-preserving public accountability dashboard — aggregate only, k-anonymity enforced, verified by dedicated tests.
- A deterministic core (routing, SLA, escalation, dedup) that AI cannot bypass, with AI used only where it adds real value (classification, evidence relevance, verification).
- Citizens can now verify their phone via OTP and get a persistent, no-password account to submit and track every report they've filed — built and shipped end-to-end, including live AWS infrastructure changes (a new DynamoDB GSI, IAM policy updates, and coordinated Lambda + Amplify deploys) with zero downtime.
- A real WhatsApp intake pipeline handling voice, photo, video, and location, in two languages.
What we learned
Deterministic-by-default is the right default for anything with legal/operational consequences (SLA clocks, escalation, who gets notified) — save the AI for judgment calls (is this photo actually a pothole?) where a wrong answer just means one clarifying question, not a broken audit trail. We also learned to treat "prepared but not deployed" as a first-class state for infrastructure changes — JWT rotation and IAM/GSI changes are safe to write and test locally but should never auto-apply to production without an explicit, informed go-ahead.
What's next for CivicKural
- Live Meta WhatsApp Business verification and SES domain verification (both pending real-world account setup).
- Backfilling historic reports into the new phone-lookup index.
- Extending citizen accounts with saved addresses/wards for faster repeat reporting.
- Municipal authority API integrations to replace the current clearly-labelled simulated authority adapter.
Built With
- amazon-amplify
- amazon-api-gateway
- amazon-bedrock
- amazon-cognito
- amazon-dynamodb
- amazon-eventbridge
- amazon-location-service
- amazon-ses
- amazon-sns
- amazon-transcribe
- amazon-web-services
- aws-kms
- aws-lambda
- boto3
- cloudformation
- fastapi
- jwt
- nextjs
- pydantic
- pytest
- python
- react
- strands-agents
- typescript
- whatsapp-business-api
Log in or sign up for Devpost to join the conversation.