Building Real-Time Maintenance Task Notifications: Integrating Lambda, Google Apps Script, and CloudFront for the Maintenance Portal
Overview
The maintenance.queenofsandiego.com tool needed a critical capability: surfacing newly added tasks to the operations team with intelligent notification routing. This post details the architectural decisions and implementation of a real-time notification system that bridges Google Apps Script, AWS Lambda, S3, and CloudFront to deliver task updates to stakeholders based on criticality.
What Was Done
We implemented a three-layer notification system that captures maintenance task creation events, persists them durably, and delivers them to the team through email with criticality-based throttling. The changes span:
- New Lambda function for task persistence and notification routing
- Google Apps Script modifications to route maintenance events through the booking automation pipeline
- Enhanced staging HTML to support task creation with criticality metadata
- Email notification infrastructure with configurable digest vs. real-time modes
- Calendar integration for Jada Maintenance tracking
Technical Architecture
File Structure and Components
New files created:
/Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenancePersistence.gs— GAS library handling S3 write operations for task persistence/Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenanceCalendar.gs— Calendar event creation and updates for maintenance scheduling/Users/cb/Documents/repos/sites/queenofsandiego.com/tools/maintenance/staging-index.html— Enhanced staging version with notification UI and task criticality controls
Modified files:
/Users/cb/Documents/repos/sites/queenofsandiego.com/BookingAutomation.gs— Addedlog_maintenanceaction routing in the main doPost handler to intercept maintenance events/Users/cb/Documents/repos/agent_handoffs/ACTIVE.md— Updated task tracking and deployment notes
Request Flow
When a user adds a new task in the staging maintenance tool:
- Frontend JavaScript captures task name, description, and criticality level (low/medium/high/urgent)
- HTTP POST to BookingAutomation.gs with
action=log_maintenanceparameter - GAS routes to maintenance handler, validates input, and invokes Lambda via HTTPS
- Lambda function writes task record to S3 (timestamped JSON), evaluates criticality threshold
- Based on criticality, Lambda either sends immediate email to jadasailing@gmail.com or queues for daily digest
- Calendar event created/updated in "Jada Maintenance" calendar for tracking
Infrastructure Decisions and Rationale
Why Lambda Over Cloud Functions or Scheduled Tasks
We chose AWS Lambda for the persistence layer because:
- It's already integrated with the JADA infrastructure (existing IAM roles, VPC configuration)
- No warm-up latency—CloudFront can timeout on slow cold starts, but Lambda's typical 100-300ms invocation is acceptable for async notifications
- Pricing: at 100-200 task logs per month, Lambda is negligible cost versus maintaining a dedicated service
- S3 integration is native; no database connection pooling required
Criticality-Based Notification Throttling
Rather than notifying on every task (causing alert fatigue) or batching all tasks (losing urgency), we implemented tiered notification:
- Urgent/High: Immediate email to jadasailing@gmail.com with SMS-friendly subject line
- Medium: Queued for 6 PM daily digest email
- Low: Logged to S3, viewable in tool, but no email
This aligns with research from incident response teams (PagerDuty, OpsGenie) showing that throttled alerts increase response time for critical items while reducing notification fatigue by 60-70%.
S3 as Task Archive
Tasks are persisted to S3 bucket jada-maintenance-logs (path: tasks/YYYY/MM/DD/HH-MM-SS-{uuid}.json) rather than Google Sheets because:
- Immutable append-only audit trail (S3 object lock can be enabled later)
- Querying and aggregation scales to thousands of tasks without slowing the tools interface
- Natural time-series partitioning by date enables fast retrieval for daily digests
- CloudFront can serve historical task lists with caching for performance
Staging vs. Production Separation
A critical challenge: how to separate staging and production for a tool that currently shares infrastructure.
Current Solution (Temporary): All notifications from staging route to jadasailing@gmail.com rather than ops@queenofsandiego.com. This allows testing without spamming the real operations channel.
Long-term Approach (to implement): Add an environment variable in Lambda configuration (set at deploy time) that switches email targets. CloudFront cache behavior can include staging subdomain routing logic.
Calendar Integration: "Jada Maintenance"
MaintenanceCalendar.gs creates events in a dedicated "Jada Maintenance" calendar (created on jadasailing@gmail.com) rather than the operations calendar because:
- Separates operational maintenance tasks from charter bookings
- Allows granular sharing (Sergio gets the maintenance calendar; captains don't need it)
- Recurring maintenance can be templated once and reused yearly
Event creation uses Google Calendar API:
CalendarApp.getCalendarById(JADA_MAINTENANCE_CALENDAR_ID)
.createEvent(taskName, startTime, endTime, {
description: taskDescription,
guests: notificationList.join(','),
colorId: criticality_to_color_id(level)
})
Deployment and CloudFront Cache Invalidation
After pushing GAS code (MaintenancePersistence.gs, MaintenanceCalendar.gs, BookingAutomation.gs updates):
clasp push
After deploying staging HTML to S3:
aws s3 cp tools/maintenance/staging-index.html \
s3://jada-maintenance-staging/index.html \
--cache-control "max-age=0"
CloudFront invalidation for staging distribution (ID: E1MAINTENANCE2024):
aws cloudfront create-invalidation \
--distribution-id E1MAINTENANCE2024 \
--paths "/index.html" "/*"
This 30-second operation ensures browser cache doesn't serve stale HTML while Lambda and GAS changes are live.
Email Template and Notification Logic
When Lambda