See all customer cases
● Case study NurturingData-cleaning

How Cashmyrr Turned CRM Data Into a Scalable, Deliverability-Safe Campaign Machine for TGV Lyria

TGVLyria needed a fast CRM migration across 3 languages, under SNCF compliance. We migrated 159K contacts, merged 4K duplicates, and protected domain reputation.

Résultat

4 350

duplicate contacts merged

Résultat

367

duplicate companies merged

Résultat

159K

contacts migrated from Sarbacane to HubSpot, then Brevo

● Context

The client: TGV Lyria, high-speed rail between France and Switzerland

TGV Lyria operates high-speed trains between France and Switzerland. Behind every Paris–Geneva or Paris–Zurich connection, a small communications team runs high-volume campaigns across three markets and three languages, France, French-speaking Switzerland, and German-speaking Switzerland, under the strict process constraints of two major rail groups: SNCF and CFF.

When the time came to change emailing platform, "good enough" wasn't an option. A strategic commercial campaign was already booked in the calendar.

The problem: a CRM migration that couldn't afford a single misstep

When we kicked off the project, the situation was textbook "fast and reliable, you have to pick both":

  • A migration on a deadline. Move from Sarbacane to Brevo without losing data quality, deliverability, or usability with a major commercial push already scheduled.
  • Three tools, none of them talking cleanly. Sarbacane for legacy sending, HubSpot as the CRM core, Brevo planned as the new transactional and marketing platform. No consistent contact view across the three.
  • Years of messy data. Source data lived in flat CSV/Excel files split by language. Opt-ins stored as "Oui", "Ja", "1" and a dozen other variants. Complaints, NPAI (postal returns), unsubscribes and bounces all mixed back into the same lists as active contacts.
  • Two large-group compliance layers. SNCF and CFF brought their own cybersecurity, GDPR, and internal validation workflows on top of standard B2C marketing operations.

The risks were just as clear: lose the ability to send mass communications, degrade domain reputation through bad-quality data, or end up with a workflow heavier than the one we were replacing.

The ask: rebuild the data and email stack on HubSpot + Brevo, clean years of legacy noise, and ship before the next campaign window, without interrupting a single send.

● The Cashmyrr solution

Why HubSpot + Brevo (and not Sarbacane v2)

Sarbacane handled the sending well enough, but it didn't solve the underlying problem: TGV Lyria's CRM was a collection of imports, not a database. We needed two things at once:

  1. A CRM core that holds a clean, structured contact model, with native segmentation, lifecycle stages, parent/child company relationships, and full GDPR traceability. HubSpot.
  2. A bulk-sending platform with strong deliverability tooling, native authentication (DKIM/SPF/DMARC), IP warming, and bi-directional sync with the CRM. Brevo.

Concretely, this combination let us ship three things Sarbacane alone never could:

  • A single source of truth for all three markets, with opt-ins, lifecycle stages, and language fields aligned.
  • A dedicated sending subdomain (newsletter.lyria.com) that protects the main domain reputation regardless of campaign volume.
  • Bi-directional sync between Brevo (sends, bounces, unsubscribes) and HubSpot (the source of truth) every ~6 hours, with workflow-based suppression to keep deliverability high.

● Steps

The architecture in production

The new stack has four layers:

  1. A staging database (SQL + MongoDB) where raw CSV/Excel files are normalised before they ever touch HubSpot.
  2. An n8n ingestion workflow that does smart upserts into HubSpot, checking what already exists, updating only missing fields, never overwriting good data with worse data.
  3. HubSpot as the structured source of truth, with B2B parent/child accounts, lifecycle stages, and source-based auto-segmentation.
  4. Brevo on a dedicated subdomain for bulk sending, with native bi-directional sync back to HubSpot.

The only custom layers are the staging pipeline and the n8n ingestion controller. Everything downstream is native HubSpot and Brevo.

The migration, step by step

Step 1 - From flat files to a controlled staging area

Importing legacy CSVs straight into HubSpot would have baked years of noise into the new system. Instead, we routed everything through a SQL + MongoDB staging layer first.

MongoDB aggregation pipelines ($project, $unionWith, $lookup) handled the heavy lifting:

  • Normalisation: opt-ins collapsed to a single boolean, dates standardised, fields aligned across FR/DE/EN sources.
  • Dynamic exclusion: emails flagged in complaints and npai collections were filtered out before reaching HubSpot.
  • Format scrubbing: phone-number-as-email entries (0606060606@icloud.com) and other deliverability-killing addresses removed at this stage.

Only data that passed staging made it downstream.

Step 2 - Smart upserts into HubSpot

We didn't trust an "Import All" button with a CRM this loaded. We built an n8n workflow as the ingestion controller:

  • The workflow queries HubSpot in batches via API.
  • For each email, it checks whether the contact already exists.
  • It then updates only missing fields or creates a new contact — never overwriting better data with worse.

The result: TGV Lyria ended up with an enriched CRM, not an overwritten one.

Step 3 - A dedicated subdomain + full authentication

Bulk campaigns were re-anchored on infrastructure that protects the main domain:

  • Dedicated subdomain: newsletter.lyria.com, separated from transactional and corporate mail.
  • Full authentication stack: DKIM, SPF, DMARC configured for maximum inbox placement.
  • Bi-directional sync: native Brevo ↔ HubSpot sync every ~6 hours, so hard bounces and unsubscribes from Brevo land back in HubSpot automatically.
  • Suppression workflows in Brevo that remove hard and soft bounces ahead of every first send.

Step 4 - Cleaning the legacy database with clustering + AI

Years of imports had created a fuzzy-duplicate problem that simple email-based dedup couldn't solve:

  • jean.dupont@company.com vs j.dupont@company.fr
  • Typos and alternate spellings on first/last names
  • B2B/B2C profiles mixed together

We layered three stages:

1. Deterministic clustering - grouping potential duplicates by three keys: exact email, domain + last name, phone + last name. This produced focused clusters without merging anything yet.

2. AI-assisted scoring on the grey zone - for pairs with initial similarity between 0.60 and 0.84, we used GPT to assess semantic similarity: nickname vs full name, typical typos, swapped first/last name, abbreviated names. GPT returned a confidence score, and only pairs at ≥ 0.85 were merged automatically. Everything below stayed in a manual review queue.

3. Smart merge rules - field by field, never destructive:

  • Keep the most valid email.
  • Keep the most advanced lifecycle stage (Customer > Opportunity > Lead).
  • Preserve the existing owner where it made sense.

Step 5 - A B2B parent/child account model

For B2B data, we restructured around a parent/child company model:

  • Companies sharing the same root_domain (e.g. hermes.com) were identified.
  • A script then designated a parent company (typically the head office) and linked local entities as child companies.

Sales got a global view of key accounts without losing the local contact context.

Step 6 - Fixing the data at the source

Cleaning legacy data is one thing. Stopping new pollution is what makes it sustainable.

Several "home-made" inputs were injecting low-quality contacts directly into HubSpot - pure HTML forms, search bars, and other on-site features that triggered automatic contact creation. The result: thousands of "Organic Leads" with no usable info and no consent. Marketing cookies were also being set before user opt-in, raising GDPR concerns.

The most severe pain point was structural: HubSpot was treating every input field as a lead source. Search queries, portal logins, contact form submissions, all created new contacts and companies in the CRM.

We fixed it in two moves:

1. API lockdown. Disabled HubSpot's "Non-HubSpot Forms" capture. Only intentional, controlled touchpoints can now create contacts.

2. Conditional tracking via CookieFirst. Rewrote the GTM integration so the HubSpot tracking script and the hubspotutk cookie only fire after explicit consent through the CookieFirst CMP.

On top of that, every new contact is auto-segmented by source and intent through HubSpot workflows: a contact from the newsletter form is tagged as a B2C target; a contact from Salesforce or with a specific Owner is tagged as B2B. Reporting and campaign relevance start clean from contact creation.

The guardrails: documentation as a deliverable

A project distributed across n8n workflows, Python/SQL scripts, MongoDB pipelines and HubSpot configuration has one critical failure mode: knowledge living only in people's heads.

We treated documentation as a first-class deliverable from day one:

  • Technical documentation in Markdown describing the what (architecture, workflows, scripts) and the how, with JSON snippets for key n8n workflows, MongoDB queries, and their underlying logic. Any developer joining later can understand the system without reverse-engineering it.
  • Maintenance runbooks, step-by-step procedures for Brevo sync log checks, handling new duplicates, and other recurring operations. Not theoretical notes, actual day-to-day playbooks.
  • Decision traceability, merge rules and AI thresholds documented in plain language. If a contact merge is ever challenged, the underlying logic is auditable.

● Result

What shipped, the output in production

Zero campaign interruption. The full migration shipped without skipping a single send, despite the strategic commercial push already on the calendar.

A structured foundation across three markets and languages, ready for high-volume campaigns on Brevo, with opt-ins, lifecycle stages, and language fields aligned across FR / FR-CH / DE-CH.

A protected email infrastructure, dedicated subdomain, full DKIM/SPF/DMARC, native bi-directional sync with HubSpot. Domain reputation isolated from campaign volume.

Dramatically lower data pollution risk, thanks to staging, deterministic + AI deduplication, and source-level lockdown of uncontrolled web inputs.

A B2B account model that surfaces parent/child company relationships and supports key-account views.

Clear documentation and runbooks that make internal teams operationally self-sufficient and resilient to staff changes.

Cashmyrr was able to propose an action plan that was effective, realistic, and aligned with our timing constraints, especially a strategic commercial campaign already in the calendar.

We benefited from a dual expertise: marketing and project leadership with Antoine, and deep technical support with Telmo.

Availability, expertise, and agility in projects. There is a real sense of building alongside the client.

— TGV Lyria

What we learned migrating high-volume B2C on HubSpot + Brevo

What worked

  • Staging before HubSpot, every time. Treating HubSpot as the destination, not the processing engine, was the single decision that made the rest of the project tractable. Cleaning fields, normalising opt-ins and applying suppression lists in MongoDB is fast, cheap and auditable. Cleaning the same data inside HubSpot would have meant workflows on workflows, lower performance and higher cost.
  • AI on the grey zone, not on everything. Deterministic clustering handled the majority of duplicates. GPT was only invoked for the ambiguous middle band (0.60–0.84 similarity). Predictable cost, predictable behaviour, and a single band of decisions to audit.
  • n8n as the ingestion controller. Smart upserts via API beat any bulk import. We chose what to update, what to leave alone, and how to log every operation, none of which a default HubSpot import gives you.
  • Documentation as a deliverable, not an afterthought. Markdown technical docs and operational runbooks made the handover boring, which is exactly what you want on a project this distributed.

The limits we hit

  • HubSpot's default behaviours need active lockdown. Out of the box, HubSpot creates contacts from sources you didn't intend to be lead sources. Non-HubSpot Forms capture, the tracking script, the cookie set before consent, all of it required explicit configuration. If you don't address this on day one, your "clean" CRM dirties itself again within weeks.
  • Native Brevo ↔ HubSpot sync runs every ~6 hours. Fine for marketing campaign cadence, slightly tight for fast feedback loops on send issues. We compensated with Brevo-side suppression workflows ahead of every first send.
  • Multi-language sources don't normalise themselves. FR/DE/EN data came with their own opt-in vocabularies and date formats. Normalisation rules had to be explicit, language by language, in the staging layer, never assumed.

● FAQ