# Gift Campaign — Personalized French email sender One-shot tool to send Giftbit gift cards to a list of contacts with a branded French email, bypassing Giftbit's English-only built-in delivery. ## How it works 1. You generate the gifts in Giftbit (UI or API) and export/receive a CSV containing one `gift_url` per recipient (one of the standard Giftbit column names: `gift_url`, `gift_link`, `url`, `link`, `redemption_url`). 2. You produce a CSV of contacts with columns `firstname, lastname, email, description`. The repo has a Python helper for this — see how `giftbit-contacts-A-first-email.csv` was generated. 3. This script matches the two CSVs (by row order, the default) and sends one personalized French email per recipient via Mailjet SMTP. 4. A `results-.csv` is written next to the script with per-row `status` (`sent` / `failed` / `dry-run`), error message, and timestamp. The Giftbit redemption landing page (where the recipient picks a brand) is controlled by Giftbit — set the campaign language to `fr-CA` in their UI or via their API so the page itself is French. ## Setup ```bash cd scripts/campaigns npm init -y # one-time, creates package.json npm install nodemailer # the only dependency ``` ## Dry run (no emails sent, HTML written for preview) ```bash node send_gift_campaign.js \ --gifts /path/to/giftbit-gifts.csv \ --contacts /path/to/giftbit-contacts-A-first-email.csv \ --template ./templates/gift-email-fr.html \ --subject "🎁 Un cadeau pour vous, de la part de Gigafibre" \ --amount "50 $" \ --expiry "31 décembre 2026" \ --from "Gigafibre " \ --dry-run ``` A `preview-YYYY-MM-DD-HH-MM/` directory will be created with one HTML file per recipient (numbered + email-suffixed). Open a few in a browser to validate the rendering on real data, then drop the `--dry-run` flag to actually send. ## Live send ```bash # Pull SMTP creds from the hub env (same Mailjet account as ERPNext) source <(ssh root@96.125.196.67 'grep -E "^SMTP_" /opt/targo-hub/.env' | sed 's/^/export /') node send_gift_campaign.js \ --gifts /path/to/giftbit-gifts.csv \ --contacts /path/to/giftbit-contacts-A-first-email.csv \ --template ./templates/gift-email-fr.html \ --subject "🎁 Un cadeau pour vous, de la part de Gigafibre" \ --amount "50 $" \ --expiry "31 décembre 2026" \ --from "Gigafibre " \ --smtp-host in-v3.mailjet.com --smtp-port 587 \ --smtp-user "$SMTP_USER" --smtp-pass "$SMTP_PASS" \ --throttle-ms 600 ``` `--throttle-ms 600` = roughly 100 emails/minute, safely below the Mailjet free-plan ceiling of ~120/min. Adjust upward to 250 ms if you're on a paid Mailjet plan. ## Matching strategies | `--match-by` | Behaviour | | --- | --- | | `row` (default) | Line N of the gifts CSV pairs with line N of the contacts CSV. Use when Giftbit issued the gifts in the same order as your contacts. | | `email` | Join by `email` column present in both CSVs. Use when Giftbit included emails in their export (more robust to ordering mistakes). | ## Template variables The HTML template at `templates/gift-email-fr.html` uses `{{var}}` syntax. Variables resolved at send time: | Variable | Source | | --- | --- | | `{{firstname}}` | contacts CSV `firstname` column (falls back to "cher client") | | `{{lastname}}` | contacts CSV `lastname` | | `{{email}}` | contacts CSV `email` | | `{{description}}` | contacts CSV `description` (we put the service address there) | | `{{gift_url}}` | matched from the gifts CSV | | `{{amount}}` | `--amount` CLI flag (e.g. `"50 $"`) | | `{{expiry}}` | `--expiry` CLI flag (e.g. `"31 décembre 2026"`) | The template uses a vintage `{{#expiry}} ... {{/expiry}}` block for the optional expiry line — currently rendered as plain text (the script's simple `{{var}}` renderer doesn't strip the tags). If you don't want the expiry sentence, edit the template directly to remove that block. ## Source data — the two CSVs ### Contacts (what we send to) Generated from a service-address selection by `scripts/campaigns/contacts_from_legacy.py` (or by hand). One row per recipient: ```csv firstname,lastname,email,description Marc-André,Boileau,boileau.marcandre@gmail.com,15 Rue des Hirondelles Maryse,Roy,roy.maryse@hotmail.com,32 Rue des Hirondelles ``` ### Gifts (output from Giftbit) Whatever shape Giftbit gives you. The script auto-detects the URL column from the common naming conventions. Typically: ```csv gift_id,gift_url,amount gb_abc123,https://app.giftbit.com/g/x7K2N9...,5000 gb_def456,https://app.giftbit.com/g/p2H8M4...,5000 ``` ## After sending Check `results-.csv`: - `status=sent` rows landed in Mailjet's outbound queue (delivery to the recipient's mailbox is not guaranteed — see Mailjet console for bounces). - `status=failed` rows have the SMTP error in the `error` column. Common causes: malformed email address, hard bounce from a stale legacy email. - Re-run only the failed rows by filtering the results CSV and feeding it back through the script. ## What's NOT in this script (intentional MVP scope) - No persistence to ERPNext doctype (no `Gift Campaign` records created) - No click tracking — the `gift_url` is included verbatim. Giftbit gives you redemption status via their own API/dashboard. - No ops UI — pure CLI. If we end up running gift campaigns regularly, wrap this in a `services/targo-hub/lib/gift-campaign.js` endpoint and add a page in ops. For now, one-shot CLI is sufficient.