Filter Sync By Segment to Any Destination

Filter Sync By Segment to Any Destination

Filter Sync By Segment to Any Destination

Photo of Utku Zihnioglu

Utku Zihnioglu

CEO & Co-founder

Open the sync list of any growing RevOps team and you'll see the same pattern five times: "Paying customers → HubSpot," "Paying customers (excluding annual plans) → Customer.io," "Paying customers, US only → ads," "Paying customers, last active 30d → CSM tool," "Paying customers, no support tickets open → upsell campaign." Each one is a separately configured sync with its own SQL snippet or filter UI, and each one drifts a little further from the others every quarter. Nobody set out to build that. It's what happens when there's no way to filter sync by segment as a first-class concept.

The fix isn't a better filter editor. It's recognizing that "paying customers" is one idea owned by one person, and the syncs should reference that idea by name.

Why teams who don't filter sync by segment end up with duplicate pipelines

The first sync is fine. Filter on plan != 'free' AND status = 'active', point it at HubSpot, done. The second sync, two weeks later, needs almost the same audience but with one change: exclude customers on the annual plan because annual renewals are handled by a different team. You copy the filter, edit one clause, save it under a slightly different name.

By the time you're at audience five, the SQL in sync one no longer matches the SQL in sync three. Someone added a trial_extended IS NULL clause to one of them six months ago because of a launch promo, and it never got cleaned up. The marketing team thinks "paying customers" means one thing. The CSM tool thinks it means something else. Nobody notices until a customer who upgraded last week shows up in the wrong campaign.

This is not a bad-engineering problem. It's a missing abstraction. There is no shared definition of "paying customer" anywhere in the stack. Each sync has its own private opinion.

The other failure mode is slower but worse: audiences that exist in marketing's head don't make it into syncs at all, because asking engineering to add a new SQL filter to a sync config is a ticket, and tickets take a week. So the audience gets exported as a CSV from one tool, uploaded to another, and now you have a stale snapshot pretending to be a live segment.

How to filter sync by segment instead of writing per-sync SQL

The shift is small in code and big in operations. Instead of every sync owning its own filter, segments live on the unified customer profile. A segment is a named, saved definition like "active paying customers in the US, not on the annual plan, no open tickets." Anyone with access can read it, edit it, or attach it to a sync.

When you filter sync by segment, the sync config doesn't say "WHERE plan = X AND status = Y." It says "include segment: active-paying-us." If the definition of "active-paying-us" changes next month, every sync pointing at it updates. No deploys, no SQL edits, no twelve PRs to keep in line.

A few things fall out of this naturally:

  • Marketing and product can own segments without an engineer. The segment lives in the UI. The sync config just references it.

  • Audiences stop drifting. There's exactly one definition of "paying customer," and it's the one every sync uses.

  • CSV exports stop. If the segment is live on the profile, every connected destination can read it. There's no reason to download.

  • Audit becomes possible. You can ask "which syncs use the 'paying customer' segment?" and get an answer in one query.

This is what teams running mature CDPs usually mean by "audience-based sync." The catch is that most platforms ship audiences as a one-audience-to-one-destination concept. Send audience A to email, audience B to ads, audience C to the CRM. That's still better than per-sync SQL, but it's not the same as making segments reusable filters on any sync.

Include vs exclude rules when you filter sync by segment

The naive model treats segments as positive lists: "send these customers to this tool." That covers maybe 60% of real cases. The rest need the opposite: "send everyone except these."

The classic example is the do-not-contact list. You want every active customer in your marketing automation tool, except the 200 people who unsubscribed, asked to be deleted, or are flagged as competitor accounts. Modeling that as an include list is painful because the include list is "everyone minus a small set" and has to be recomputed every time the small set changes. Modeling it as an exclude is one line: include active-customers, exclude do-not-contact.

A few include/exclude patterns that show up over and over:

Use case

Include segment

Exclude segment

Cold email tool

New free signups

Churned customers, internal users

CSM platform

Paying accounts > $5k ARR

Trials, expired

Upgrade campaign

Free tier, active last 30d

Already-upgraded, do-not-contact

Ad audience

Lookalike seed segment

Existing customers, suppressed users

Webhook to billing system

All accounts

Test/internal accounts

Notice that "do-not-contact" and "internal accounts" appear as excludes across multiple syncs. They get defined once and reused. The first time you do this, the relief is real. There is finally one place to update when legal sends you another suppression list.

Order matters: includes run first, then excludes are subtracted. A customer in both lists is excluded. This is the behavior you almost always want, but it's worth being explicit about it in the sync UI so nobody has to guess.

Where the filtered segment lands: CRM, email, warehouse, ads

Different destination tools expect segments in different shapes, and the sync layer has to translate. The segment definition stays the same. How you send a segment to a destination varies by tool.

When you send a segment to a CRM, it usually maps to a property on the contact: segment_active_paying_us = true. The CRM can then filter views and workflows on that property without knowing how the segment is defined. When the customer leaves the segment, the property flips to false. Nothing gets deleted. The CRM just sees a property change, and downstream automations react.

For email and marketing automation tools, the segment often becomes a list or tag. Customers in the segment get added to the list; customers who leave get removed. Some tools handle this gracefully; others need explicit add/remove calls per customer per run. The sync engine should hide this difference.

For warehouses, segments are sometimes written as a column on a profile table, sometimes as membership rows in a join table. Analysts then SQL against it. This is the one case where the "everything is SQL" instinct is correct, because the consumer is a SQL tool. Write it both ways and let analysts pick.

For ad platforms, segments become custom audiences. The sync uploads matching identifiers (hashed email, mobile ad ID) to the ad tool's audience API. The ad tool then decides whether to match them against its own user base. This is the only destination where you generally can't observe what happened to a specific customer. Past the upload boundary, the ad platform is a black box.

Across all four, the principle is the same: the segment is defined once on the profile, and the sync handles the translation to whatever shape the destination expects. Reforge calls this pattern "audience activation". A segment becomes useful at the moment it can be applied somewhere downstream, not when it's first defined.

Keeping segments and syncs in sync when audiences change

Segments are not static. Marketing renames "paying customers" to "active paying accounts." Product adds a new plan tier and the definition of "premium" has to be updated. Someone realizes the do-not-contact list was missing the unsubscribed segment from the last campaign.

When a segment definition changes, three things have to happen:

  1. Membership has to be re-evaluated. Customers who newly match the segment start getting written on the next run. Customers who no longer match stop getting written. If you're using segment membership as a CRM property, the property has to flip.

  2. Downstream tools have to handle the transition. A customer dropping out of a marketing list mid-campaign is fine. A customer dropping out of a billing webhook segment mid-invoice is a bigger problem. The sync layer has to know which destinations care about transitions.

  3. Sync owners have to know. If marketing edits a segment that drives a billing sync, the billing team should at least see a notification. Otherwise edits ripple silently.

The trap is treating segment membership as a snapshot taken at sync time. It is, but the snapshot has to be honest about what changed since last time. Old membership minus new membership = customers to remove from the destination. New minus old = customers to add. Same customers in both = no-op. This is the kind of diff that breaks when each sync owns a custom SQL filter, because nobody is computing it across the whole stack. Each sync sees only its own state.

A related case worth mentioning, and then I'll stop: renamed segments. Someone renames "paying-customers" to "active-paying-customers" thinking it's clearer. Now every sync referencing the old name is broken. The right behavior is to either treat segment references as IDs (so the rename is cosmetic) or to surface a warning on every sync that points at a missing segment. Letting the sync fail silently is the worst option, and somehow the most common one.

There's also a whole literature on market segmentation that predates any of this tooling. The operational problem we're describing is downstream of that. Once you've decided who your segments are, can your sync layer actually act on them? In most stacks, the answer is "kind of, if you keep an eye on it." That's where the operational pain is, and it doesn't get fixed by adding more segments. It gets fixed by making segments first-class everywhere.

Where to go from here

If your team is at the "one sync per audience" stage, the next move is small: pick the audience that's defined in three different places and consolidate. Define it once on the profile, point the existing syncs at the new definition, delete the per-sync filters. You'll find one of the three was subtly wrong. Better to find that on a Tuesday than during a quarterly review.

Oneprofile lets you define a customer segment once on the unified profile and attach it as an include or exclude filter on any sync. Same segment, many destinations. When marketing edits the segment, every downstream sync picks up the new definition automatically. Stale or renamed segments surface as warnings on the sync form before they cause damage. Segment filters are on every plan, including the free tier.

The bigger shift is cultural. The team that owns the audience definition should not be the team that owns the sync config. Decouple those two responsibilities and the duplication problem mostly evaporates on its own.

What does it mean to filter sync by segment?

Is this the same as audience-based sync in a CDP?

What happens when a segment definition changes mid-sync?

How is filter sync by segment different from a SQL filter on a sync?

Can I exclude one segment from a sync while including another?

Ready to get started?

No credit card required

Free 100k syncs every month