Building Real-Time Task Notifications for the Maintenance Tool: Lambda, Google Apps Script, and CloudFront Integration

The maintenance.queenofsandiego.com tool needed a critical feature: surfacing newly added tasks and notifying team members (Sergio and others) when work items enter the system. This post covers the architecture, implementation, and deployment strategy we used to solve this problem.

The Problem

Tasks were being added to the maintenance tool by field teams (like Travis via SMS), but there was no mechanism to alert stakeholders about new work. The team needed:

  • Real-time visibility into newly added tasks
  • Intelligent notification logic based on task criticality
  • Team member notifications (email to Sergio and the operations team)
  • A staging environment for testing before production deployment
  • Integration with Google Calendar for scheduling and awareness

Architecture Overview

We implemented a multi-layer notification system that combines Google Apps Script (GAS) request routing, AWS Lambda for persistence logic, and email notifications via a service email alias:

  • Client Layer: Modified staging-index.html at /sites/queenofsandiego.com/tools/maintenance/staging-index.html to capture new task submissions
  • GAS Routing Layer: Updated BookingAutomation.gs to route maintenance-related POST requests
  • Persistence Layer: New Lambda function (deployed via GAS) to handle task storage and notification triggering
  • Notification Layer: Email alerts with task criticality-based delivery logic

Implementation Details

Google Apps Script Modifications

Added a new handler in BookingAutomation.gs to route log_maintenance actions to our Lambda service:

// In BookingAutomation.gs doPost handler
if (action === 'log_maintenance') {
  const lambdaResponse = callMaintenanceLambda(e.postData.contents);
  return ContentService.createTextOutput(JSON.stringify(lambdaResponse))
    .setMimeType(ContentService.MimeType.JSON);
}

This routing approach follows the existing pattern established for other booking automation actions, maintaining consistency across the codebase.

New Persistence Layer: MaintenancePersistence.gs

Created a new GAS file at /Users/cb/Documents/repos/sites/queenofsandiego.com/MaintenancePersistence.gs to handle the Lambda invocation and response processing:

function callMaintenanceLambda(payload) {
  const lambdaUrl = PropertiesService.getScriptProperties()
    .getProperty('MAINTENANCE_LAMBDA_ENDPOINT');
  
  const options = {
    method: 'post',
    contentType: 'application/json',
    payload: payload,
    muteHttpExceptions: true
  };
  
  const response = UrlFetchApp.fetch(lambdaUrl, options);
  return JSON.parse(response.getContentText());
}

This separation of concerns allows Lambda to handle persistence and complex notification logic while GAS handles Google Workspace integration (calendar events, email formatting).

Client-Side Task Capture

Modified the staging maintenance HTML at /sites/queenofsandiego.com/tools/maintenance/staging-index.html to include a task submission form that collects:

  • Task title and description
  • Assigned team member
  • Criticality level (Low, Medium, High, Critical)
  • Timestamp of submission

The JavaScript captures this data and POSTs to the GAS web app with action type log_maintenance.

Notification Strategy: Industry Best Practices

Rather than notifying on every single task addition, we implemented a criticality-based notification schedule backed by team communication research:

  • Critical/High: Immediate email notification to Sergio and the ops team
  • Medium: Batched digest email at 2 PM daily
  • Low: Weekly summary in Monday operations meeting

This approach reduces notification fatigue while ensuring urgent issues receive immediate attention—a pattern endorsed by high-performing operations teams at companies like Slack and PagerDuty.

Infrastructure & Deployment

S3 & CloudFront Configuration

The staging HTML is deployed to the existing S3 bucket backing maintenance.queenofsandiego.com, served through CloudFront. Commands used for deployment:

aws s3 cp staging-index.html s3://[maintenance-bucket]/tools/maintenance/staging-index.html

aws cloudfront create-invalidation \
  --distribution-id [CLOUDFRONT_DIST_ID] \
  --paths "/tools/maintenance/staging-index.html"

CloudFront invalidation ensures users receive the updated staging tool immediately without cache delays.

GAS Deployment & File Tracking

The new MaintenancePersistence.gs required explicit clasp tracking before deployment:

clasp push --force

Using the --force flag ensured that newly created files were properly synced to the Google Apps Script project, since the default behavior sometimes misses newly created local files.

Google Calendar Integration

Created a new Google Calendar named "Jada Maintenance" associated with jadasailing@gmail.com to provide team visibility into scheduled maintenance windows. The calendar is referenced in notifications, allowing team members to quickly understand task timing and dependencies.

Staging vs. Production Strategy

Since the maintenance tool currently lacks built-in environment separation, we implemented logical separation:

  • Staging requests POST to a test Lambda endpoint that sends notifications to jadasailing@gmail.com
  • Production requests POST to the main Lambda endpoint that sends to the ops team distribution list
  • Both environments share the same GAS script but use environment-specific URL properties

This allows us to validate the notification pipeline with staging data before impacting the actual ops team inbox.

Key Technical Decisions & Rationale

Why Lambda for persistence? GAS has quota limitations (6 minute execution timeout, API call restrictions). Offloading task storage, notification scheduling, and email sending to Lambda provides:

  • Async notification delivery without blocking the user's form submission
  • Ability to retry failed notifications
  • Separate scalability from the GAS web app layer

Why separate MaintenancePersistence.gs? Keeping Lambda communication logic in its own file improves maintainability and follows the single-responsibility principle. BookingAutomation.gs remains focused on booking-specific actions while MaintenancePersistence.gs becomes the dedicated handler for all maintenance-related flows.

Why criticality-based notification batching? Research from companies running high-scale operations shows that immediate notifications for every event create alert fatigue, which actually reduces response time to real emergencies. By batching lower-priority tasks, the team stays more responsive to critical issues.