Auditing and Rebuilding a Stalled Funeral Home Outreach Campaign: Infrastructure, Data, and Process Gaps
Executive Summary
During a recent infrastructure audit of our funeral home outreach initiative, we discovered a critical gap: zero emails have been sent despite two partial implementations existing in the codebase. This post details the technical investigation, root causes, and the architectural decisions needed to resurrect the campaign.
What We Found: Two Incomplete Implementations
Implementation #1 – Google Apps Script (GAS)
Located at /repos/sites/queenofsandiego.com/FuneralOutreach.gs, this implementation:
- Reads prospect data from Google Sheet
1ADx_0L6...rg38, tabContacts(25 prospects manually loaded) - Sends via AWS SES using in-script SigV4 signing from
admin@queenofsandiego.com - Implements a widening-gap cadence: Day 1 → Day 4 → Day 10 → Day 19 follow-ups
- Tracks opens, bounces, OOO responses, and replies across 14 columns
- Critical issue: The trigger was never installed. No
InitialSenttimestamps exist across all 25 rows. - Schema mismatch: The sheet has only 9 columns, but
funeralOutreachSetup()expects 14. The initialization function was never executed.
Implementation #2 – EC2 Cron Daemon
A secondary system exists at /home/ubuntu/repos/tools/send_funeral_blast.sh:
- Originally scheduled as a one-off cron:
5 16 24 4 *(April 24 only) - Invokes
jada_blast.py send --campaign funeral-outreach-2026againsttools/contacts/funeral-homes-sd.csv(8 hand-entered prospects) - Uses template at
tools/templates/funeral-outreach.html - Critical issue: No evidence of execution. The campaign ledger at
s3://progress.queenofsandiego.com/blast-campaigns.jsoncontains 2,649 emails across 9 campaigns, butfuneral-outreach-2026is absent. - No log files in
tools/logs/matching the expectedfuneral_blast_*.logpattern, suggesting the cron either never ran or failed silently at the approval gate.
Root Cause Analysis
Why No Emails Sent?
- GAS trigger missing: The script exists but has no time-driven trigger installed. In Google Apps Script, code doesn't execute without an explicit trigger (time-driven, form submission, etc.).
- Schema incomplete:
funeralOutreachSetup()` was never called, leaving the sheet in an inconsistent state relative to the script's expectations. - Sender mismatch: The GAS implementation sends from
admin@queenofsandiego.com, but the standing requirement is to useoutreach@burialsatseasandiego.comvia SES. This domain transition was never completed. - EC2 cron one-off: The cron was a single-fire job on April 24, not a recurring task. Even if it had executed, it would have been a one-time blast, not a daily 10-prospect harvester as specified in the standing rule.
- Missing prospect harvester: Neither implementation includes logic to discover new prospects daily. The GAS version relies on manual sheet updates; the EC2 version uses a static CSV.
Data Validation
Gmail audit (last 180 days, jadasailing@gmail.com):
- 0 sends to any of the 25 funeral home addresses
- 0 sends from
outreach@orinfo@burialsatseasandiego.com - 0 inbound replies from prospect domains (greenwoodsd.com, lavistamemorialpark.com, neptunesandiego.com, etc.)
This confirms: the campaign never launched.
Technical Decisions for Resurrection
Path A: Quick Start (20 minutes)
Install the GAS trigger as-is, let it fire Wednesday at 9 AM PT from admin@queenofsandiego.com
Path B: Proper Implementation (recommended, ~2 hours)
Before sending a single email, complete these steps:
- Provision
outreach@burialsatseasandiego.comin AWS SES- Create a new SES verified identity for the domain
- Request production access (required for high-volume sends)
- Configure DKIM signing
- Store SES credentials in Secrets Manager (not in code)
- Extend the Google Sheet schema
- Add missing columns to match the 14-column model in
FuneralOutreach.gs:InitialSent,F1Sent,F2Sent,F3Sent,Replied,Bounce,OOO,Unsubscribe,Notes - Call
funeralOutreachSetup()to initialize tracking columns - This ensures that when the script runs, it can write send timestamps and response flags back to the sheet
- Add missing columns to match the 14-column model in
- Update
FuneralOutreach.gs` to use the correct sender- Replace
admin@queenofsandiego.comwith the SES-verifiedoutreach@burialsatseasandiego.com - Update the SES region endpoint if needed (default: us-west-2)
- Validate that the sender is whitelisted in SES before trigger deployment
- Replace
- Change cadence from weekly to daily-10
- Install a daily time-driven trigger at 9 AM PT instead of weekly Wednesday
- Update
runFuneralOutreach()` to exit after processing max 10 new rows per day - This matches the standing rule and prevents accidental bulk sends
- Decommission the EC2 cron
- Comment