I Connected My CRM to an AI Assistant That Sends Birthday Cards Automatically

A build log. No pitch, no fluff — just what I built, what broke, and whether it was worth it.

The Problem

I run a small agency. Every client birthday, I’d have the same internal debate: should I send something? A card felt right but took 20 minutes I didn’t have. An email felt cheap. So I’d do nothing — and then feel guilty when a competitor’s “Happy Birthday!” landed in the client’s inbox before mine.
The math was embarrassing. We had ~60 active clients. Remembering 60 birthdays manually is a systems failure waiting to happen. So I systemized it.

The Stack

Three pieces, all already in my life:
  1. HubSpot (our CRM) — already stores every client’s name, email, and birthday
  2. Greetu’s MCP server — exposes card creation, scheduling, replies, and reactions as callable tools
  3. A 40-line Python script — runs nightly, talks to both
No Zapier, no webhook gymnastics, no enterprise middleware.

The Build

Step 1: Get the API key

Grabbed a key from greetu.io/mcp — grtu_... format, one header, done.

Step 2: Export birthdays from the CRM

HubSpot exports birthdays as a contact property. One filtered view + weekly CSV export. The CSV has exactly the columns I need: firstname, lastname, email, birthday.

Step 3: The nightly script

The core loop is embarrassingly simple. Every night at 2am, a cron job runs this:
Python

import csv, requests
from datetime import datetime

API_KEY = "grtu_xxxxxxxx"
MCP = "https://greetu.io/api/mcp"

def mcp_call(method, params=None):
    return requests.post(MCP, headers={
        "Content-Type": "application/json",
        "X-API-Key": API_KEY,
    }, json={"jsonrpc": "2.0", "id": 1, "method": method,
             "params": params or {}}).json()

today = datetime.now().strftime("%m-%d")

with open("birthdays.csv") as f:
    for row in csv.DictReader(f):
        if row["birthday"][5:] == today:  # MM-DD match
            mcp_call("tools/call", {
                "name": "greetu.create_card",
                "arguments": {
                    "recipient_name": row["firstname"],
                    "occasion": "birthday",
                    "use_ai_message": True,
                    "sender_name": "Our team at [Agency]",
                }
            })
Every client with a birthday today gets a card with an AI-written, personalized message. Unique per person — not a template.

Step 4: The weekly human pass

This part matters. Every Monday morning, I skim the week’s queued cards via greetu.list_cards. Takes ~5 minutes for a week of cards. I edit any message that feels off, add a voice note for VIP clients (yes, the MCP accepts Base64 audio), and let the rest fire.

Step 5: Closing the loop

The part I didn’t expect to love: greetu.get_replies and greetu.get_reactions. When a client opens their card and reacts or replies, it comes back through the same MCP endpoint. That feedback now feeds a simple Slack digest. The team sees “🎉 Maria reacted ❤️ to her card” in real time. Morale boost, unexpected.

What Broke

Honest section, because build logs without failure are fiction:
  • Date formats. My CRM exported MM/DD/YYYY, my script expected ISO. Cards silently didn’t fire for two test runs. Fixed with one strftime change.
  • Duplicate sends. Cron ran at 2am; my laptop slept. Missed runs piled into a catch-up burst that double-sent two cards. Solution: a tiny sent_log.json dedupe.
  • AI tone. Early AI messages were… fine. Generic-fine. Adding sender_name and more CRM context (industry, past project notes) to the arguments made messages noticeably warmer. Context in, warmth out.

Was It Worth It?

Sixty seconds of honesty: yes, and it wasn’t close.
Setup took one evening. Ongoing cost is the Monday 5-minute review. And the results are the kind that don’t show up neatly in a dashboard — a client calling to say “that card made my day,” a reply on a card from someone we hadn’t heard from in months, a renewal conversation that started with “by the way, loved the birthday thing.”
Retention economics say a retained client is worth far more than the cost of a card. But honestly? I’d keep it running just for the replies.

Try It

The whole stack is smaller than it sounds: a CSV, a key, and a nightly script. If you can write a for loop, you can build this.
👉 Grab a Greetu API key and see the MCP docs: greetu.io/mcp — the create_card, schedule_card, and get_replies tools I used are all documented there, free to start.

Leave a Reply

Your email address will not be published. Required fields are marked *