Integration · Marketing
How to build an ad audience sync
A customer sees an advert asking them to try the product they already pay for. The thinking behind audiences that know better, the prompts that build it, and what it takes to keep it running.
Built with Tray Headless
- System Salesforce
- Step Build from CRM state
- Step Suppress
- Step Hash locally
- System LinkedIn Ads
Suppression is applied before the audience leaves, and identifiers are hashed locally so raw email never reaches the platform.
The short answer
What is an ad audience sync?
Done well, an ad audience sync is four things: audiences built from live CRM state rather than an exported list, suppression applied before anything leaves so customers and open deals are never targeted, identifiers hashed locally so raw email never reaches the platform, and a refresh cadence fast enough that the audience is not stale. The part that goes wrong most often is suppression. Advertising acquisition to an existing customer wastes the spend and tells them your systems do not talk to each other.
What matters here
- Never upload an exported list. It is stale the moment it is downloaded and it cannot be suppressed.
- Suppress customers, open opportunities, recent churns and anybody who opted out, before the audience leaves your systems.
- Hash identifiers locally. Raw email should never reach an ad platform, and hashing at the boundary is the cheapest privacy control there is.
- Refresh at least daily. An audience refreshed monthly is advertising to people whose situation changed three weeks ago.
- Track match rate per platform. A low match rate means you are paying for reach you are not getting.
Who this is for
You run marketing operations or demand generation. Audiences are exported to CSV, uploaded by hand, and nobody is quite sure whether current customers are in them.
How it works in practice
The sequence, from a segment being defined to an advert being served.
- 1
The audience is defined as CRM state, not a list
A saved query evaluated at run time, so somebody who converted this morning leaves it today.
- 2
Suppression runs before anything leaves
Existing customers, open opportunities, recent churns, opt-outs and competitor domains.
- 3
Identifiers are hashed locally
Normalised then hashed before transmission, so raw email never reaches the platform.
- 4
The audience is synced as a delta
Adds and removes, rather than replacing the whole list and resetting the platform learning.
- 5
Match rate is recorded per platform
Because you are paying for reach and a poor match means you are not getting it.
- 6
Suppression is re-evaluated on every refresh
A customer who signed yesterday leaves the acquisition audience today, not next month.
What audience sync is made of
Four parts, though the second is the one customers notice when it is missing.
Live CRM state
A query evaluated at run time. An exported list is a snapshot of a moment that has already passed.
Suppression before sending
Customers, open deals, churns, opt-outs and competitors, applied inside your systems instead of hoped for at the platform.
Local hashing
Normalise and hash at the boundary. The platform gets a hash, never an address, and that is the cheapest privacy control available.
Delta refresh
Adds and removes rather than a full replace, so platform optimisation is not reset every night.
The Tray Headless prompts
Paste these into Claude Code or Codex with the Tray Headless plugin installed. Each stage runs on its own. The systems named in them are the worked example rather than a requirement, and every prompt says so.
Once per project, run
/tray-workflows:set-workspace
to pick the workspace these build in. Point it at a sandbox first.
- 1
Start here: define suppression first
Suppression is the reason to build this instead of exporting a CSV.
Headless skills
build-workflowUse build-workflow. The systems in play are Salesforce and the ad platforms we run, or whatever we run in those seats. Before defining any audience, define the suppression list: Anybody at an account with an active subscription Anybody at an account with an open opportunity above a stage Anybody who churned in the last year, which is a win-back audience instead of an acquisition one Anybody who opted out of marketing Employees, competitors and partners by domain Tell me where each of those lives. Suppression is the reason to build this instead of exporting a spreadsheet, so it gets designed first.
- 2
Build audiences from live state, never a list
An exported list is stale before it finishes downloading.
Headless skills
build-workflowUse build-workflow. Define each audience as a saved query against the CRM and the warehouse, evaluated at run time. Never accept a static uploaded list. Somebody who converted this morning should leave the acquisition audience today, and a list cannot do that. Apply suppression after building the audience and before anything leaves our systems. Do not rely on the platform to exclude a list you also uploaded, because the exclusion rules differ per platform and the failure mode is invisible. Log the suppression counts per run. If suppression removes a large share of an audience, the audience definition is wrong rather than suppression working hard.
- 3
Hash before it leaves
The cheapest privacy control available.
Normalise then hash every identifier before transmission: lowercase and trim the email, strip plus-addressing, normalise phone numbers to E.164, then hash with the algorithm each platform specifies. Raw email addresses should never reach an ad platform. Hashing at the boundary costs nothing and removes an entire category of problem from your data processing agreements. Send the minimum each platform needs for matching. Do not send company, job title or anything else because a field happened to be available.
- 4
Sync deltas and keep the audience alive
A full replace resets platform learning every night.
Headless skills
tray-gotchasUse tray-gotchas, then sync as a delta: adds and removes against what the platform already holds, rather than replacing the audience wholesale. A full replace resets the platform's optimisation and it shows in performance within days. Refresh at least daily. Handle these: An audience that falls below the platform minimum size, which silently stops serving rather than erroring A platform rejecting a batch, which must not leave the audience half updated Removals failing, which is the dangerous direction because it means a customer is still being targeted An audience that has not refreshed within its window, which should alert rather than quietly go stale
- 5
Measure match rate and suppression
Because you are paying for reach you may not be getting.
Report per audience and per platform: size, match rate, suppression counts by reason, and how long since the last successful refresh. Match rate is the number that matters commercially. A 40% match means more than half the audience is not reachable and the spend is concentrating on whoever did match, which is rarely the segment you designed. Alert on: match rate dropping sharply, an audience below the minimum serving size, a refresh that has not completed, and any run where suppression removed nothing, because that usually means the suppression join silently broke.
- 6
Check it end to end, then hand the definitions over
Because audiences change with every campaign.
Run the per-step schema checks and the whole-workflow audit before this touches production. Run once in preview, showing the audience and the suppression counts without sending, and have somebody read it. Then open the same workflow in Tray Build so marketing operations can define new audiences and amend suppression rules in the visual canvas.
What it connects to
The CRM decides who, the warehouse adds context, and the platforms receive hashes.
Salesforce
Read accounts, contacts, opportunities and opt-out state, which is what both the audience and the suppression are built from.
Reads
Snowflake
Add product usage and segmentation the CRM does not hold, and record match rates over time.
Reads and writes
Marketo
Read engagement, so a retargeting audience can exclude people already in an active nurture.
Reads
Slack
Alert on a stale audience, a collapsed match rate or a suppression join that removed nothing.
Writes
Same build, other stacks
The design does not change if you run something else in one of these seats. The same prompts build it against Microsoft Dynamics 365, Google BigQuery, Microsoft Teams, HubSpot, Databricks or Google Chat.
Named systems are the ones most teams run, not the only ones that work. Each is an authentication in your Tray workspace, referenced by name, so the workflow never holds a credential. Where we have a connector page, the name links to it.
Running it in production
This sends customer identifiers to third parties and spends money. Both need care.
It lives on the platform, not in a terminal window
Daily rebuilds with suppression re-evaluated each time run on the same engine, instead of a person exporting a CSV when they remember.
Every send is recorded
Which audience, how many, what suppression removed, and the match rate returned. That record is what a privacy review asks for.
Credentials are managed, never written into the build
Ad platform credentials can spend money and upload customer data. They live in your workspace, scoped per platform and separately revocable.
Marketing owns the definitions
Audience queries and suppression rules open in Tray Build, so a new campaign does not need an engineer.
Hashing happens before the boundary
Raw identifiers never leave your systems. It costs nothing and removes a whole category of problem from the data processing agreement.
Questions people ask
Why not upload an exported list?
Because it is stale the moment it downloads and it cannot be suppressed. Somebody who becomes a customer that morning stays in the acquisition audience until the next manual upload.
Why suppress inside your own systems?
Because platform exclusion rules differ and fail silently. Applying suppression before the audience leaves means a customer cannot be targeted even if a platform setting is wrong.
Why hash locally?
Because raw email should never reach an ad platform. Normalising and hashing at the boundary costs nothing and removes an entire category of question from a privacy review.
Why sync deltas rather than replacing?
Because a full replace resets the platform optimisation, and the performance drop shows within days. Adds and removes keep the audience learning intact.
What should be monitored?
Match rate per platform, and any run where suppression removed nothing. A low match rate means you are paying for reach you are not getting, and zero suppression usually means a join broke.
Further reading
Background on the same subject, for the case rather than the build.
Related guides
Marketing
How to build a campaign attribution pipeline
Stitch touches to accounts, publish more than one model, and reconcile spend against pipeline so the number survives a finance review. The prompts.
Revenue operations
How to build a CRM to marketing automation sync
Decide field ownership before you build, sync deltas not everything, handle the person-versus-lead model gap, and break loops. The prompts that build it.
Marketing
How to build a webinar and event registration sync
Get registrations in before the event, split attended from registered, route the follow-up the same day, and attribute honestly. The prompts that build it.
Last reviewed September 2026.