Birthday Genie
An AI assistant that remembers birthdays and writes the messages

Overview
A relationship-care SaaS where users chat with an AI assistant (on the web or over WhatsApp) that stores who matters to them, generates personalised birthday messages, and automatically delivers reminders on the right day in each user's local timezone across WhatsApp and email.
My role
Full-stack build of the entire product: the Prisma/PostgreSQL data model, the Next.js web app, an Express (OOP repository/service/controller) API, a standalone BullMQ worker, the OpenAI integration, Stripe billing, WhatsApp/email delivery, and the containerised CI/CD deployment pipeline.
The problem
People forget the birthdays of those they care about, and even when reminded, struggle to write something that sounds like them rather than a generic template. Standard reminder apps surface a date but leave all the actual effort (remembering, composing, and sending on time across timezones) to the user.
The solution
Users add the people who matter simply by chatting in natural language, on the web or over WhatsApp; GPT-4o-mini extracts each person's name, relationship, and birthday. The system pre-generates personalised birthday messages, then a background worker delivers timezone-aware reminders automatically via WhatsApp and/or email. The codebase was deliberately split from a Next.js monolith into three isolated services (web, API, worker) so background delivery scales and fails independently of the user-facing app.
Key features
- Conversational person capture on web and WhatsApp: GPT-4o-mini parses free-text ("my mum's birthday is 5 March"), gated behind a cheap regex so an LLM call is only spent when the message looks like an add.
- AI birthday-message generation: three variations per person per calendar year (idempotent, cached), built from relationship, name, computed age, and stored fun facts.
- WhatsApp assistant over Twilio: a numbered-menu bot plus signature-verified inbound webhooks, phone opt-in linking, and pre-approved content templates with wa.me deep links to the recipient.
- Timezone-aware automated reminders: a BullMQ worker scans upcoming birthdays and fires day-before and birthday-day notifications at 08:00 in the user's local time, with automatic email fallback when WhatsApp fails permanently.
- Calendar subscription: an RFC 5545 ICS feed of yearly-recurring all-day events, served from a public per-user token for webcal subscription in Google/Apple calendars.
- Subscription billing: Stripe (GBP) with free/pro/pay-as-you-go tiers, free-tier quota enforcement, and an append-only credit ledger.
Architecture & engineering
An npm-workspaces + Turborepo monorepo with a shared Prisma client package. Three Docker services: a Next.js web app (thin proxy + RSC), an Express API holding all business logic, and a BullMQ worker with no HTTP port. Webhooks use a verify-then-forward pattern: Stripe/Twilio signatures are verified in the web app, then the parsed event is forwarded to the API. NextAuth v5 issues session tokens that the Express API decodes to authenticate requests. Delivery is timezone-aware via Intl.DateTimeFormat. Deployed to a single Hostinger KVM 2 VPS behind host nginx + certbot; GitHub Actions builds three GHCR images and SSH-deploys via docker compose, running prisma migrate deploy before app start. Production runs self-hosted PostgreSQL; staging uses Neon.
The hardest problem
Reliable multi-channel delivery through third-party providers. Twilio WhatsApp can fail permanently (unregistered number, template rejection) or transiently. The worker classifies Twilio error codes: permanent codes skip retries and trigger an automatic email fallback via Resend, while transient codes re-throw so BullMQ retries with exponential backoff; a Twilio status webhook feeds delivery state (queued/sent/delivered/read/failed) back onto each notification record.
Key learnings
Splitting a Next.js monolith into isolated web/API/worker services makes background delivery independently deployable and scalable. LLM cost is controllable by gating model calls behind cheap heuristics and caching generated output. And any delivery channel that depends on a third party (WhatsApp) needs an explicit fallback and error-code taxonomy to be dependable.
Want to see it in action?
Explore the live product, or reach out to talk through the build.