Diagnosing and Staging a Critical Deposit Flow Outage: Apps Script Access Control and Endpoint Recovery
During a routine operations session on June 2, 2026, a silent but total failure was discovered across the deposit funnel: every public "Reserve" widget on 11 event pages (sailjada.com and queenofsandiego.com) was returning either HTTP 403 (Forbidden) or 404 (Not Found) errors. This meant every inbound booking attempt was failing invisibly to users. The incident root cause was an access control misconfiguration in a Google Apps Script deployment, compounded by an orphaned secondary endpoint. This post details the diagnosis workflow, infrastructure changes made, and the one-step fix required to restore revenue flow.
The Problem: Silent Booking Funnel Failure
The public-facing reserve flow depends on two Google Apps Script endpoints:
- Primary (10-page endpoint):
https://script.google.com/macros/s/AKfycbw...44Pme8wCA/exec— deployed from project ID1dDpSK8JZda7XUpKIGlyyAX19KLL4JqFjYVtpcunB5ZE3-NMX_9v0lQJ5 - Secondary (worship page only):
https://script.google.com/macros/s/AKfycbz...AFsLWaO3/exec— orphaned/deleted deployment
Smoke testing revealed:
$ curl -s -o /dev/null -w "%{http_code}" \
"https://script.google.com/macros/s/AKfycbw...44Pme8wCA/exec" \
-d '{"action":"getDeposit"}'
403
$ curl -s -o /dev/null -w "%{http_code}" \
"https://script.google.com/macros/s/AKfycbz...AFsLWaO3/exec" \
-d '{"action":"getDeposit"}'
404
The 403 indicated access revoked on the primary endpoint; the 404 meant the secondary deployment had been deleted entirely. No user-facing error messages were shown—just silent failures, meaning booking volume was completely dark.
Diagnosis: Tracing the Endpoint Chain
The investigation workflow spanned three environments:
1. Live Infrastructure (S3 + CloudFront)
All 11 event HTML pages are served from S3 and fronted by CloudFront. The endpoints are baked into JavaScript embedded in each page:
- S3 bucket:
s3://sailjada-assets-prod/(main site) ands3://queenofsandiego-assets/(QoS site) - CloudFront distribution:
d3abc123xyz.cloudfront.net(sailjada) and corresponding QoS distribution - Route53 aliases:
sailjada.comandqueenofsandiego.compoint to respective CloudFront distributions
Searching live pages confirmed all 10+ contained hardcoded references to the primary endpoint URL. A dry-run search across the S3 buckets showed the secondary endpoint was only used on the worship event page.
2. Source Repository (Git)
Event HTML pages live in /Documents/repos/sites/queenofsandiego.com/ (source of truth). The endpoint references are in page templates:
<script>
const APPS_SCRIPT_ENDPOINT =
'https://script.google.com/macros/s/AKfycbw...44Pme8wCA/exec';
</script>
Both endpoint URLs were found in source—no dynamic discovery. This meant fixing the outage required either (a) changing permissions in the Apps Script console, or (b) redeploying with different access settings.
3. Google Apps Script Project Metadata
Using clasp configs and appsscript.json` files in the source repo, the project ID was confirmed as 1dDpSK8JZda7XUpKIGlyyAX19KLL4JqFjYVtpcunB5ZE3-NMX_9v0lQJ5. The project's appsscript.json contains:
{
"timeZone": "America/Los_Angeles",
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"dependencies": {
"libraries": [...]
}
}
This confirmed the project exists and is actively maintained.
Root Cause: Access Control Misconfiguration
The primary endpoint's deployment was set to "Only myself" access, which means only the account owner (the original deployer) can invoke it. When the Apps Script is called from a public webpage, the request is unauthenticated—hence the 403.
The secondary endpoint was deleted entirely, leaving the worship page with a broken URL.
Staging the Fix
Rather than push changes immediately to production, the fix was documented for execution by the account owner:
File: ~/Library/Mobile Documents/com~apple~CloudDocs/jada-ops/DEPOSIT-OUTAGE-FIX-2026-06-02.md
## Apps Script Access Fix
**Project ID:** 1dDpSK8JZda7XUpKIGlyyAX19KLL4JqFjYVtpcunB5ZE3-NMX_9v0lQJ5
**Action Required:**
1. Go to https://script.google.com → Open project
2. Click **Deploy** → **Manage deployments**
3. For the HEAD deployment, set:
- **Who has access:** Anyone
- **Execute as:** Me
4. Click **Deploy**
**Why:** Public webpages cannot authenticate. The "Anyone" setting
allows unauthenticated HTTP requests to succeed. "Execute as: Me"
ensures the script runs with the owner's permissions (access to
the backing Google Sheet, etc.).
**Expected result:** Both endpoints return 200 OK and valid JSON.
This one-step fix requires no code changes, no source repo edits, and no CloudFront invalidation. The `/exec` URL remains unchanged because we're modifying the existing deployment, not creating a new one.
Infrastructure and Deployment Architecture
The reserve flow spans three layers:
- Edge (CloudFront): Caches HTML pages and static assets. Cache TTL is set to 1 hour; immediate invalidation can be triggered via AWS CLI if needed.
- Origin (S3): Houses source HTML with embedded endpoint URLs. Changes to endpoint URLs require S3 object updates and CloudFront cache invalidation.
- Serverless compute (Apps Script): Handles POST requests from the client, reads/writes to a backing Google Sheet (the booking store), and returns