How to Personalize Birthday Offers from MongoDB into Klaviyo Events
Build a Spojit workflow that runs every morning, finds customers whose birthday is today in MongoDB, generates a personalized offer with AI, and pushes a birthday event with a coupon code to Klaviyo so your flows can send the email.
What This Integration Does
Birthday emails are one of the highest-converting lifecycle messages, but they only feel special when the offer and copy are tailored to each customer. This workflow reads your customer records straight from MongoDB, where you already keep birthdays and lifetime spend, asks an Agent-mode Connector node to write a short personalized offer line and pick a coupon tier per customer, then sends a create-event call to Klaviyo. In Klaviyo you build a flow triggered by that metric, so the actual send, throttling, and deliverability stay where your marketing team manages them. Spojit handles the daily selection and personalization; Klaviyo handles the sending.
The workflow is driven by a Schedule trigger that fires once a day in your timezone. On each run it queries MongoDB for profiles whose birthday month and day match today, loops over the matches, generates an offer for each one, and records a Klaviyo event per customer. It does not store state between runs: re-running the same day pushes the same customers again, which is why the event includes a stable unique_id so Klaviyo deduplicates and your customers are not emailed twice.
Prerequisites
- A MongoDB connection added in Spojit (Connections → Add connection) with read access to the database and collection holding your customers.
- A customer collection where each document has an email and a birthday you can match on. This guide assumes fields named
email,firstName,birthMonth,birthDay, andlifetimeSpend. Adjust field names to your schema. - A Klaviyo connection with a private API key that has write access to events and profiles.
- A metric name reserved in Klaviyo for these events (this guide uses
Birthday Offer) and a Klaviyo flow triggered by that metric. - Credits available for AI generation, since the personalization step uses an Agent-mode Connector node.
Step 1: Start the workflow on a daily Schedule trigger
Add a Trigger node and set its type to Schedule. Use a 5-field cron expression and an IANA timezone so the run lands at a sensible local hour. For 7am on every day in Sydney, set the cron to 0 7 * * * and the timezone to Australia/Sydney. The trigger output is { scheduledAt }, which you can reference downstream as {{ trigger.scheduledAt }} if you want to stamp the event time.
Step 2: Compute today's month and day
Add a Connector node in Direct mode using the date utility connector and the parts tool against {{ trigger.scheduledAt }}, or use now with your timezone. The goal is two integers you can match in MongoDB: the current month and day of month. Save the result so you can reference {{ today.month }} and {{ today.day }} in the next step. Computing the date inside the workflow keeps the query timezone-correct rather than depending on the database server clock.
Step 3: Find today's birthday customers in MongoDB
Add a Connector node in Direct mode, pick the MongoDB connector, and choose the find-documents tool. Set collection to your customer collection name and pass a filter that matches the stored month and day fields. Use projection to return only what you need, and a limit high enough to cover a busy day.
{
"collection": "customers",
"filter": {
"birthMonth": {{ today.month }},
"birthDay": {{ today.day }},
"email": { "$exists": true, "$ne": "" }
},
"projection": {
"email": 1,
"firstName": 1,
"lifetimeSpend": 1
},
"limit": 500
}
The tool returns the matched documents. Save the result as birthdays so the array is available as {{ birthdays.documents }} (use the field name your result exposes). If your birthdays are stored as a single date value instead of split month and day fields, store them split or precompute them, because a year-agnostic match is far simpler than date arithmetic in the filter.
Step 4: Loop over each matched customer
Add a Loop node in ForEach mode over the array from Step 3. Inside the loop body, each iteration exposes the current customer, for example {{ customer.email }}, {{ customer.firstName }}, and {{ customer.lifetimeSpend }}. Everything in the following steps runs once per birthday customer. Keeping the daily volume modest (a single day's birthdays) means the loop stays well inside normal run limits.
Step 5: Generate a personalized offer with AI
Inside the loop, add a Connector node in Agent mode and define a Response Schema so the output is reliable JSON you can map into the Klaviyo event. The agent reads the customer's first name and lifetime spend and returns a coupon tier plus a one-line offer message. Use a prompt like the one below, and let the agent fill the schema fields couponCode, discountPercent, and offerMessage.
Write a warm, one-sentence birthday offer for {{ customer.firstName }}.
Lifetime spend is {{ customer.lifetimeSpend }}.
If lifetime spend is over 500, give a 25 percent coupon; otherwise 15 percent.
Return couponCode (uppercase, like BDAY-XXXX), discountPercent, and offerMessage.
Save the node result as offer. Because Agent mode with a Response Schema forces structured output, you can rely on {{ offer.couponCode }}, {{ offer.discountPercent }}, and {{ offer.offerMessage }} existing on every iteration. If you prefer fully deterministic coupon logic with no AI cost, you can instead derive the tier with a Condition node and only use AI for the message.
Step 6: Push the birthday event to Klaviyo
Still inside the loop, add a Connector node in Direct mode, pick the Klaviyo connector, and choose the create-event tool. Provide attributes containing the metric name, the profile (matched by email so Klaviyo finds or creates the profile), your offer details under properties, and a unique_id so repeated runs on the same date collapse to one event.
{
"attributes": {
"metric": { "name": "Birthday Offer" },
"profile": { "email": "{{ customer.email }}" },
"properties": {
"coupon_code": "{{ offer.couponCode }}",
"discount_percent": {{ offer.discountPercent }},
"offer_message": "{{ offer.offerMessage }}",
"first_name": "{{ customer.firstName }}"
},
"unique_id": "birthday-{{ customer.email }}-{{ today.month }}-{{ today.day }}",
"time": "{{ trigger.scheduledAt }}"
}
}
In Klaviyo, build a flow with a trigger of the Birthday Offer metric. The flow's email can pull coupon_code, discount_percent, and offer_message from the event properties, so the personalization generated in Spojit shows up in the customer's inbox.
Step 7: Notify yourself that the run finished
After the loop, add a Send Email node to send yourself a daily summary. Set Recipients to your address, a Subject like Birthday offers sent for {{ today.month }}/{{ today.day }}, and a body noting how many customers were processed. The Send Email node uses Spojit's built-in mail service, so no extra connection is needed, and the recipient must be on your org allowlist under Settings → General → Email recipients. This gives you a quiet confirmation each morning that the schedule fired and customers were queued.
Tips
- Store birthdays as separate month and day fields (or precompute them) so the MongoDB
filterstays a plain equality match instead of date math. - Keep the AI prompt tight and use a Response Schema so every iteration returns the same fields; this prevents one malformed offer from breaking the Klaviyo mapping.
- Let Klaviyo own throttling and send-time optimization. Spojit's job is daily selection and personalization, not delivery.
- Use Miraxa to scaffold the loop and connector nodes by describing this workflow in a sentence, then fine-tune fields in the properties panel.
Common Pitfalls
- Timezone drift: if you compute the date from a server clock instead of the Schedule trigger's timezone, customers near midnight get their offer a day early or late. Derive month and day from
{{ trigger.scheduledAt }}in the chosen timezone. - Duplicate emails on re-run: the workflow keeps no state, so a manual re-run resends the same day. Always include a stable
unique_idin the Klaviyo event so it deduplicates. - Leap-day birthdays: customers born on 29 February never match in non-leap years. Decide whether to treat them as 28 February or 1 March and adjust the filter.
- Missing or malformed emails: profiles without a valid email cannot be matched in Klaviyo. Filter them out in the MongoDB query and validate before the event call.
Testing
Before enabling the schedule, temporarily insert one test customer document with today's month and day and your own email, then use the Run button to run the workflow manually. Confirm the MongoDB step returns exactly your test record, that the AI step produced a coupon and message, and that the Klaviyo event appears on your profile's activity feed under the Birthday Offer metric. Once the single record flows end to end and your Klaviyo flow fires, remove the test document and enable the Schedule trigger.