A bilingual care system for one Mexican-American family.
My mom was carrying the entire memory of three elders' care alone. I wanted to build her a tool that absorbed the cognitive load — without making her family install an app, learn a UI, or speak English. Eleven days later we shipped a private SMS + web system that the whole family uses, including the grandparents who only speak Spanish.
The problem
Three elders need ongoing care: my dad (Pa), my mom-in-law (Ma), and an uncle (Ricky). Six to seven adult family members spread across two cities help out. Coordination happens in three places at once — group text, phone calls, and my mom's head.
The pattern was always the same:
- Mom takes Pa to cardiology. The doctor changes a medication. Mom remembers.
- Aunt Rosa picks up Pa for a follow-up two weeks later. Doesn't know about the medication change. Asks Mom over text.
- Mom has been in three other appointments since. Has to dig back through her memory.
- Multiply by three elders, multiply by two years.
Then potlucks: Mom and Aunt Rosa both bring tamales. Or four people sign up for landscape day and three back out the morning of, leaving Mom with rakes alone. Or Tía Carmen drives 90 minutes assuming the cleaning is on, when the date had quietly moved.
The thing every elderly-care family knows: the institutional memory shouldn't live in one person's brain. But every off-the-shelf tool either (a) requires the elders to use a smartphone app or (b) requires English fluency.
The design constraints
Before writing a line of code I locked four constraints with the family:
- SMS is the universal interface. Pa and Ma have flip phones. Aunt Rosa has an iPhone. My uncle Tomás won't install an app. SMS works for everyone — and a US local number means no carrier confusion.
- Spanish-first for elders. Ma reads Spanish. Pa reads basic Spanish. Anything they receive — confirmations, weekly digests, reminders — has to be Mexican Spanish, register appropriate for elders ("usted," not "tú"), with medical jargon translated to plain language ("presión," not "hipertensión arterial").
- One person to set up — a few minutes per family member to onboard. Each new member should be onboarded by the coordinator in under 60 seconds, no email sent, no password required. They start texting immediately.
- Private. Always private. No advertising network, no third-party analytics, no data resale. Family medical context never leaves the family.
The stack
How it works
Texting a care update
A family member sends an SMS to (989) 762-4030:
"Took Pa to cardiology today. Dr. Martinez said BP is stable, continue current meds, follow up in 6 months."
The webhook lands at a Supabase Edge Function which:
- Validates the Twilio signature against a canonical URL (Supabase rewrites
req.urlinternally — that took a day to find). - Looks up the sender's number in
contributor_phonesto get their family member record + preferred language. - Calls Claude with the message + a curated
<care_context>block (current meds, recent appointments, recent notes, upcoming events). - Claude classifies intent — query, care write, or event commitment — and emits structured JSON.
- For writes, the function inserts the appropriate rows (
care_appointments,care_medications,care_notes) and replies with a confirmation in the sender's language.
Asking a question
Same number, different intent:
"What medications is Pa taking right now?"
The function pulls Pa's active medications, hands them to Claude as context, and Claude composes a 320-char SMS-friendly answer. If the sender's preferred language is Spanish, the answer comes back in Mexican Spanish — with medication names preserved as-is and frequency ("once daily") translated to "una vez al día."
The bilingual moment that mattered
Records are saved in the contributor's original language to preserve their phrasing. Translation happens at view time. The use case I optimized for: a grandchild with weak Spanish reading the Spanish view aloud to Ma. Every Spanish translation has to be something they can read confidently — that requirement quietly drove a lot of the prompt work.
Architectural decision that mattered: care context goes in the user message, not the system prompt. A coordinator's note containing "ignore previous instructions" can't hijack the model that way. Stored prompt injection is the SMS equivalent of XSS. We caught it in /cso review before launch.
Household logistics
The medical features were live for a week before the family started telling me their other coordination problems — the potlucks, the landscape days, the cleaning rotations. So the system grew a second layer: events and event_commitments tables, the same SMS line classifying a third intent ("I'm in for Saturday"), bilingual reminders the day before each event, and helper-nudges when a coordinator says "need 4 people" and only 2 sign up.
For potlucks specifically, the system parses what each person is bringing and flags duplicates. "I'll bring tamales for Sunday's potluck" gets a reply that confirms the commit AND warns "Heads-up: Mom is also bringing tamales. Want to switch?" — solving the most common failure mode for a Mexican-American household potluck.
Pa's house live
The newest layer: a private /house page showing recent photos, what's been logged this week, who's signed up to come help. Anyone in the family can text a casual photo (garden, food, kid's birthday) and it lands on the photo board automatically — Claude classifies the image as casual-vs-medical and routes it to the right place. Remote relatives feel less remote.
What the architecture looks like
- 9 Postgres tables.
families,family_members,contributor_phones, 5care_*tables,events,event_commitments,house_photos. - Row-level security on every table. Family isolation via two SECURITY DEFINER helpers (
get_user_family_id,get_user_family_role). Anon JWT + user JWT for the API; service-role only inside admin actions after a role check. - 5 Edge Functions.
family-care-mcp(web API + admin),sms-ingest(Twilio webhook + Claude classifier),voice-ingest(transcribed voicemails forwarded to sms-ingest with a shared secret),weekly-digest(Sunday cron, bilingual),event-reminders(hourly cron — 24h reminders + 36h helper-nudges). - Soft-delete throughout. Every care/event row has
archived_at;active_*views withsecurity_invoker = truefilter. A leaked service-role key can't bypass family scoping. - Translation at view time. Records stored in original language; Claude translates on read for Spanish viewers, preserving proper nouns and converting medical jargon to plain Mexican Spanish.
The build, day by day
/plan-eng-review which caught 6 issues including the silent LIMIT 1 bug if a user joined two families./cso caught 5 issues: stored prompt injection in care context, XSS in the web view, missing CRON_SECRET on weekly-digest, x-internal-forward bypass on voice-ingest, and a missing .gitignore. All five fixed before launch.Numbers
What I'd do differently
- Build the bilingual layer earlier. Retrofitting Spanish on day 6 was twice the work of building it in from day 1. Mexican Spanish register, "usted" for elders, medical jargon translation — these are design constraints, not localization tasks.
- Run /cso before, not after. Stored prompt injection is now top-of-mind for me; I would've caught it sooner if I'd run a security review on the second day instead of the eighth.
- Soft-delete from the first migration. Adding
archived_atretroactively is fine, but I had to update queries everywhere. Patterns are cheaper to set up early.
What unlocked the speed
- Claude Code's plan/review skills.
/plan-eng-reviewon day 1 caught architectural issues that would've taken me a week to find through trial-and-error./csoran security review faster and more thoroughly than I could. - Supabase as the spine. Postgres + RLS + Edge Functions + Storage + Auth in one project meant I never had to integrate two services. Family isolation comes "free" once
get_user_family_id()is the only knob. - Boring stack on purpose. Static HTML, no build step, no framework. The web app is one HTML file per language. Vercel deploys on push. Updating a page means editing the HTML — no compile step in the way.
- Twilio's reliability. A2P 10DLC registration is the slowest moving part; everything else just worked.
If you want to talk about this: I'm Alfonso Herrada. I write at analyticgator.ai about marketing-ops + AI for small teams. The Family Care Desk is the fourth desk in a series — the other three (Career, Content, Operators) live at analyticgator.ai. Reach me at alfonso@alfonsoherrada.com.
Bilingual systemsFamily opsSMS UXClaude APISupabase RLSTwilio A2PAI-assisted dev