Inspiration

We noticed a fundamental problem on campus: nobody knows which bathrooms are actually good. Students take bathroom breaks based on luck and previous experiences. Facilities managers have no real-time visibility into bathroom conditions. This creates a frustrating experience for students who simply want to take a nice dump, and makes it impossible for facilities to proactively address supply and maintenance issues.

We wanted to build a real-time, crowdsourced campus bathroom rating system that benefits both students (finding good bathrooms) and facilities (getting alerts when maintenance is needed). The idea was simple but powerful: combine real-time crowd ratings with smart notifications.

What it does

Litterboxd is a REST API that enables:

For Students:

  • Rated bathrooms on a 1-10 scale across campus buildings
  • AI-powered summaries of bathroom conditions (powered by Google Gemini 2.5 Flash)
  • View real-time stall occupancy to find available stalls
  • Bookmark favorite bathrooms for quick access
  • Contribute reviews that help the community make better decisions

For Facilities Coordinators:

  • Receive webhook alerts when bathrooms drop below a 4.0/10 rating
  • View comprehensive bathroom metrics (average rating, supply levels, accessibility, etc.)
  • Manage bathroom registrations across campus buildings (Siebel, Grainger, CIF)
  • Track real-time occupancy from IoT sensors

For Developers:

  • Fully documented REST API with over a dozen endpoints
  • Comprehensive documentation with 40+ curl examples including success and error cases
  • Standardized error responses for easy integration
  • Ready for production with async/await, connection pooling, and comprehensive logging

Technical Capabilities:

  • AI-Powered Vibe Checks - Google Gemini generates comprehensive, easy-to-read summaries of bathroom conditions
  • Smart Webhooks - Automatic low-supply notifications
  • Real-Time Updates - Async database operations for high concurrency
  • IoT Ready - Per-stall occupancy tracking perfect for IoT sensors
  • Professional Error Handling - 6 error types with consistent JSON responses across all endpoints

How we built it

Architecture & Stack

  • Framework: FastAPI
  • Database: MySQL hosted on DigitalOcean
  • AI/LLM: Google Gemini 2.5 Flash (witty bathroom summaries)
  • Data Validation: Pydantic v2 (strong typing + runtime validation)
  • File Storage: DigitalOcean Spaces + boto3 (for future image uploads)
  • Deployment: Uvicorn (development) / Gunicorn + Uvicorn workers (production)

Key Implementation Details

Error Handling System

  • Created 6 custom exception classes
  • Standardized error responses

Smart Webhook System

  • WebhookPayload class holds notification data
  • notify_low_supply() triggers when avg_rating < 4.0
  • Logs successes and failures

14 API Endpoints

  • Bathrooms: POST (create), GET (list + filter), GET/{id} (details)
  • Reviews: POST (add with Form data), PUT (update), GET vibe-check (AI summary)
  • Stalls: POST (update occupancy), GET (list status)
  • Webhooks: POST (register), GET (list + filter), DELETE
  • Favorites: POST (add), GET (list), DELETE
  • Health: GET /health (online status)

Data Models

  • BathroomModel: building_name (Enum), floor_number, bathroom_gender, ratings, supplies
  • ReviewModel: rating (1-10), comment, user_id (unique per bathroom)
  • WebhookModel: url, event_type, retry tracking
  • FavoriteModel: user_id + bathroom_id unique constraint
  • StallModel: occupancy tracking with timestamps

AI Integration

  • generate_vibe_check() function calls Google Gemini 2.5 Flash API
  • Sends building name, floor, gender, and review comments
  • Generates witty 2-sentence summaries
  • Results cached in bathroom.ai_review field
  • Triggered on new reviews and updates

Database Setup (init_db.py, database.py)

  • Async MySQL connection
  • Auto-creates all tables on first run
  • Constraints on (building, floor, gender) for bathrooms
  • Constraint on (user_id, bathroom_id) for reviews to prevent dupes

Challenges we ran into

Form Data vs JSON for Reviews Endpoint

Challenge: The API needed to support optional image uploads (future feature) while also accepting reviews with just text. Solution: Used Form() parameters instead of JSON request body. This required all test commands to shift from JSON to -F flags, which was confusing initially but correct for multipart/form-data.

Standardizing Error Responses Across Endpoints

Challenge: Different endpoints were throwing different types of errors (HTTPException, IntegrityError, custom exceptions) making it inconsistent for clients. Solution: Created a unified error_handlers.py module with 6 custom exception classes (ValidationError, NotFoundError, ConflictError, etc.) and validation helpers. Every endpoint now returns the same standardized error schema.

Database Constraint Violations Handling

Challenge: SQLAlchemy's IntegrityError for duplicate reviews or bathrooms wasn't user-friendly. Solution: Instantiated a Caught IntegrityError Class explicitly and converted to readable errors with clear messages like "User already reviewed this bathroom" or "Bathroom already exists at this location."

Webhook Delivery and Retry Logic

Challenge: Needed automatic retry logic for failed webhook deliveries, but simple blocking retries would timeout the review endpoint. Solution: Used async httpx.AsyncClient with background notification. Implemented 3 retry attempts with 5-second delays and 10-second timeout per request.

Accomplishments that we're proud of

Accomplishments we’re proud of

Smart Webhook System

  • Automatic low-supply notifications when rating drops below 4.0
  • Retry logic (3 attempts, 5s delays, 10s timeout) implemented correctly
  • Persistent webhook registration with filtering
  • Works with webhook.site for easy testing
  • Detailed logging of all delivery attempts

Minimalistic AI Integration Done Right

  • In the age of AI slop, we steered clear of AI-centric production
  • Google Gemini 2.5 Flash generates witty, context-aware bathroom summaries
  • Triggered on new reviews, cached in database to avoid redundant API calls
  • Handles edge case (no reviews yet) gracefully

Thoughtful API Design

  • RESTful conventions followed correctly (POST for create, PUT for update, DELETE for delete, etc.), despite minimal experience
  • Proper HTTP status codes (201 Created, 204 No Content, 409 Conflict, etc.)
  • Clear request/response formats with consistency checks

Comprehensive, Accurate Documentation

  • 1,600+ line API_GUIDE.md with 40+ real curl examples
  • Every endpoint documented with success responses (200, 201, 204) and error cases (400, 403, 404, 409, 500)
  • Setup instructions that work (tested from scratch)
  • Webhook integration guide with testing steps using webhook.site
  • Clear separation of concerns: README.md (overview) + API_GUIDE.md (reference)

What we learned

Error Handling is Everything

Comprehensive error handling is absolutely key in order to have a production-level API. Developers and users need to know exactly what went wrong, which field caused the error, and what constraint was violated. Standardizing this across endpoints saves enormous time in debugging.

Documentation is as important as Code

Thorough documentation with clear examples is as important as the code itself. Developers could copy-paste and immediately understand what the API does.

Pydantic is goated

Pydantic's validation at the model level (with constraints like Field(ge=1, le=10)) catches most input errors automatically. But business logic validation (e.g., "building must be registered") still needs custom helpers.

Environment Variables Matter for Security

We started with some docs that exposed database credentials, mb. Using .env for all secrets and excluding it from git is important to avoid spiders/scrapers. This is a lesson we applied immediately.


What's next for Litterboxd

  • Image Upload/Storage - S3 integration (DigitalOcean Spaces is already configured)
  • User Profiles - Reputation system, user badges (e.g., "Bathroom Scout")
  • Trending Bathrooms - Real-time ranking based on recent reviews
  • Scrollable element - Able to browse the best and the worst of toilets!
  • Mobile App - Flutter or React Native frontend consuming this API
  • Analytics Dashboard - Facilities can see trends (which bathrooms need attention)
  • Multi-Campus Support - Expand beyond UIUC
  • Scheduled Maintenance - Facilities can schedule maintenance windows
Share this project:

Updates