Work project

Hyper-Personalized Email at Scale

Emails that send themselves, with each subscriber's name and segment

Nightly results for two lottery brands plus welcome and birthday emails, each one carrying the name and segment of the person who receives it.

Recreation of a fictional lottery's daily results email in an inbox, with one email per day, the date in the subject and the results of eight draws. (open the full-size image)
Recreated with fictional data; the real system is private.

What it is about

These automations build and send the emails of the lottery brands the agency serves, the results email every night through Amazon SES and the ones a subscriber event triggers, such as a welcome or a birthday.

The results email is built from the official API and waits for the draws published late. Recipients come from Zoho Analytics, each email carries the person's name and segment, and the delivery events come back to PostgreSQL. The results are also exposed by a Spring Boot API with Thymeleaf templates.

What it does

  • A Python script queries the results API, fills a Jinja2 template that holds only variables and writes the HTML ready for the Amazon SES template.
  • Emails that a subscriber event triggers, such as the welcome and the birthday greeting.
  • Recipients come from a Zoho Analytics view through its bulk export API; each email carries the person's name and segment, and a column of the file can assign another template.
  • When a draw is not published yet, the script exits with its own code and the pipeline retries every 10 minutes, up to 9 times, before sending what was published.
  • Sending spreads the emails across several workers with a per-second limit and, when Amazon SES asks to slow down, waits a random time that grows with each attempt.
  • It notices when the history's pagination returns repeated rows and asks for the day again with other page sizes until it is complete.
  • The email weighs 55 to 75 KB, under the size at which Gmail clips it, and the subject carries the date so Gmail does not thread every day's send together.
  • A FastAPI receiver with asyncpg stores in PostgreSQL the delivery events Amazon SNS reports for each email.
  • A Spring Boot API with Thymeleaf exposes the same results as JSON and as an HTML template.

How it works

From the API to the inbox

The nightly pipeline reads the API, takes the recipients from Zoho Analytics and sends; the delivery events come back to PostgreSQL.

  1. Source

    • Official results API
    • Paginated history of previous days
    • Recipients from Zoho Analytics
  2. Nightly pipeline

    • Cron on the server
    • Jinja2 template that holds only variables
    • Retries every 10 minutes
  3. Delivery and measurement

    • Amazon SES
    • Delivery events through Amazon SNS
    • PostgreSQL

Decisions and their costs

  1. Wait for late draws, with a limit

    The API publishes some draws more than an hour late.

  2. Templates that hold only variables

    A design change must never alter a figure, and an IDE formatter broke a template once.

  3. A subject with the date

    With a fixed subject, Gmail threads every day's send into one conversation.

  4. One template a day, each person's data at send time

    Each email carries the recipient's name.

Stack

Pick a technology to see the projects that use it.