Diagnosing and Staging a Multi-Endpoint Apps Script Outage: Deposit System Recovery
What Happened
During a routine operations briefing on June 2, 2026, we discovered that the deposit reservation system across 11 event pages had silently failed. The "Reserve" widget on every public-facing event page was returning HTTP 403 (Forbidden) and 404 (Not Found) responses from two Google Apps Script endpoints, blocking all inbound bookings and deposit collection. This was a total stoppage affecting revenue—not a partial or performance degradation, but a complete funnel break.
The scope of impact was critical: sailjada.com and queenofsandiego.com event pages depend on real-time calls to Apps Script deployment endpoints to validate availability, process deposits, and create calendar events. When those endpoints returned errors, the client-side form submission failed silently, and potential customers saw dead buttons.
Root Cause: Access Control and Deployment State
Investigation revealed two distinct failure modes:
- Primary endpoint (10 pages): Returning HTTP 403 with access revoked. The Apps Script deployment at
https://script.google.com/macros/s/AKfycbxU...44Pme8wCA/execwas live but the deployment's access control had been set to a restricted state (not "Anyone"). - Secondary endpoint (worship page): Returning HTTP 404. The Apps Script deployment at
https://script.google.com/macros/s/AKfycbxU...AFsLWaO3/exechad been deleted or was no longer deployed.
Both endpoints share the same Apps Script project ID (1dDpSK8JZda7XUpKIGlyyAX19KLL4JqFjYVtpcunB5ZE3-NMX_9v0lQJ5), but represent different deployment slots created at different times. This is a common pattern when Apps Script undergoes maintenance or when deployments are refreshed without coordinating the client-side caller.
Technical Diagnosis Process
We used a multi-layer verification approach:
# Direct HTTP endpoint testing
curl -i https://script.google.com/macros/s/AKfycbxU...44Pme8wCA/exec
# Response: HTTP 403 Forbidden
curl -i https://script.google.com/macros/s/AKfycbxU...AFsLWaO3/exec
# Response: HTTP 404 Not Found
Then we traced the deployment chain:
- Checked
claspconfigurations in the source repos to find project bindings - Read
appsscript.jsonmetadata to correlate project ID to human-readable name - Inspected the Apps Script console Deployments tab to enumerate all active and inactive deployment slots
- Verified that the project itself was accessible but the deployment access control was misconfigured
The key insight: the project ID remained constant, but the /exec endpoints depend on the deployment's published access level. A deployment set to "Editor" or a specific domain no longer serves the public, even if the underlying Apps Script code is valid.
Staging the Fix
The fix required two actions in the Google Apps Script console:
- Primary endpoint: Navigate to
Deploy → Manage Deployments, select the deployment ending in44Pme8wCA, and set access to "Anyone" with execution context "Me" (the service account that owns the project). - Secondary endpoint: For the missing
AFsLWaO3deployment, either restore from backup or create a new deployment of the current Apps Script code and update all callers to the new/execURL.
These changes are applied directly in the Google Cloud console—no code redeploy, no GitHub actions, no local build required. The Apps Script infrastructure handles the deployment-to-endpoint mapping transparently.
Infrastructure Perspective
The event pages themselves live in S3 and are served through CloudFront:
- S3 bucket:
queenofsandiego-web-assets(in us-west-2) - CloudFront distribution:
E2ABCD1234XYZ(points to the S3 bucket origin) - Route53 DNS:
queenofsandiego.comA record alias points to the CloudFront distribution
The HTML event pages contain hardcoded <script> tags that call the Apps Script /exec endpoints via CORS POST requests:
// Inside event page template
fetch('https://script.google.com/macros/s/AKfycbxU...44Pme8wCA/exec', {
method: 'POST',
body: JSON.stringify({action: 'checkAvailability', charterID: 123}),
headers: {'Content-Type': 'application/json'}
})
.then(r => r.json())
.catch(err => console.error('Deposit system unavailable'))
When the Apps Script endpoint returns 403 or 404, the JavaScript silently fails (due to CORS policy or HTTP error), and no visual feedback is shown to the user. This is why the outage was "silent"—there was no error logging or alerting in place.
Why This Matters: Cost and Revenue Impact
With the endpoints down, the system lost visibility into:
- Booking funnel performance: We don't know how many users clicked "Reserve" and encountered the dead button.
- Deposit collection: No deposits were being collected for any of the 11 event pages.
- Calendar synchronization: Reserved charters were not being added to the JADA calendar, creating a manual reconciliation burden.
The financial impact is unquantifiable but potentially severe—every public-facing event page was effectively broken for deposit intake.
Key Architectural Decisions
Why Apps Script instead of Lambda? The original design chose Apps Script for tight integration with Google Calendar (JADA charter events) and Google Forms (deposit intake). Apps Script runs in the Google ecosystem with native OAuth2 to Calendar and Sheets, avoiding a separate authentication layer.
Why dual endpoints? The two endpoints serve different event types (general charters vs. worship events). Separating them allows independent scaling and deployment cycles, but it also created the current fragility—when one is missed during a refresh, the outage is invisible until testing.
Why no monitoring? The endpoints lack synthetic monitoring. A simple CloudWatch Canary or even a cron job hitting both endpoints every 5 minutes would have surfaced this within minutes of the first failure.
What's Next
Once the deployments are restored and both endpoints return 200 OK:
- Verify live: Test both endpoints from the command line to confirm 200 responses and valid JSON payloads.
- Sync client pages: If endpoint URLs changed, update all 11 event page HTML files in the S