---
title: "Customer Sync"
description: "Synchronize and migrate customer data into Brevo with fail-closed consent proof, suppression carry-over, and operator-controlled conflict resolution."
url: https://tajo.io/docs/skills/data-sync/customer-sync/
markdown: https://tajo.io/docs/skills/data-sync/customer-sync.md
lang: en-US
---

# Customer Sync

Tajo moves customer data into Brevo two ways: **ongoing sync** (a connected platform’s changes flow into Brevo continuously) and **bulk migration** (an at-capacity, resumable transfer from Mailchimp, Klaviyo, HubSpot, Salesforce, Shopify, WooCommerce, Stripe, and others). Both run on the same engine and share one non-negotiable property: **consent is proven per record, never assumed.**

## How consent is decided

Every record passes a fail-closed consent gate before it can enter a marketing audience:

| Verdict | What happens |
| --- | --- |
| **Consent proven** (e.g. Mailchimp `subscribed`, Klaviyo `SUBSCRIBED`, Shopify marketing consent) | Imported into your chosen Brevo lists |
| **Explicitly refused** (unsubscribed, opted out) | **Not dropped** — carried into Brevo as a blacklisted contact and recorded in the do-not-contact register, so the new platform can never mail them |
| **Suppressed by deliverability** (hard bounce, spam complaint — e.g. Mailchimp `cleaned`, Klaviyo `HARD_BOUNCE`/`SPAM_COMPLAINT`) | Carried over as blacklisted and marked sticky: a later opt-in on another platform never lifts a bounce or complaint |
| **Unproven** (no consent signal at all) | Refused. “We could not prove consent” and “there is consent” are never the same outcome |

The distinction between “refused” and “unproven” matters: an unsubscribe is a decision that must survive the migration; an absent field is not a decision at all.

### Per-platform consent sources

| Platform | Consent proof | Suppression signals |
| --- | --- | --- |
| Mailchimp | Member status `subscribed` | `unsubscribed` (opt-out), `cleaned` (hard bounce) |
| Klaviyo | Profile marketing consent `SUBSCRIBED` | Suppression list with reasons (`UNSUBSCRIBE`, `USER_SUPPRESSED`, `HARD_BOUNCE`, `INVALID_EMAIL`, `SPAM_COMPLAINT`) |
| Shopify | `email_marketing_consent.state` / `accepts_marketing` | Explicit `not_subscribed` |
| HubSpot | Subscription statuses (batch API, 100 contacts per call) | `hs_email_optout` |
| Salesforce | Not provable in bulk from Sales Cloud — identity migrates, audience enrollment stays closed | `HasOptedOutOfEmail`, `EmailBouncedDate` |
| WooCommerce / Stripe | Not inferred from purchases — a customer record is not marketing consent | — |

## Conflict resolution during migration

When the source and Brevo disagree about the same address, resolution follows the mode you pick:

- **Latest decision wins (default)** — the side whose consent decision carries the newer timestamp prevails. A side with no parseable timestamp always loses to a side with one; when neither has a timestamp, the less-reachable state wins.
- **Source of truth** — you name the authoritative platform for the migration (“the source is right” or “Brevo is right”). If your workspace already declares a system of record, that platform is the default authority automatically.
- **In every mode**: hard bounces and spam complaints are deliverability facts, not preferences — they are never un-suppressed by a newer opt-in, and every conflict the engine declines to act on is counted and reported, never silently skipped.

## Reliability at capacity

- Cursor-based extraction walks the full source table (100k+ contacts) with checkpoints — a crash resumes from the last completed batch, and retries never double-write (idempotency keys per record).
- Brevo writes use the bulk import API (10,000 contacts per chunk) inside Brevo’s rate limits; suppression state travels with the import, so there is no reachable-then-suppressed window.
- Every run reports what moved and what didn’t: contacts imported, explicit denials carried, suppressions propagated, conflicts held, and per-record failure reasons.

## What sync adds after migration

Once migrated, the same connections keep Brevo current: platform webhooks (Shopify, WooCommerce, WordPress, Mailchimp) flow changes in near-real-time, and polled sources refresh on schedule. Unsubscribes observed at the source flip consent in the ledger — with the audit trail of when, from where, and on what evidence.

## Setup

1. Connect the source platform in Tajo (API key or OAuth, per connector).
2. Connect Brevo and choose the target lists.
3. Pick the conflict-resolution mode (or accept the default).
4. Run the migration; review the report — including held conflicts — before enabling automations.

Consent and suppression handling is not configurable off. There is no setting that imports unsubscribed contacts as mailable.
