Integration · Legal and compliance
How to build a consent and preference sync
Somebody unsubscribes and gets another email nine days later because a second system never heard about it. The thinking behind consent that propagates, the prompts that build it, and what it takes to keep it running.
Built with Tray Headless
- System Preference centre
- Step Consent store
- Step Propagate now
- System Marketo
- System Salesforce
One system owns consent and every other one reads it, with withdrawal propagating in seconds instead of on the next nightly run.
The short answer
What is a consent and preference sync?
Four things make up a consent and preference sync: one authoritative store instead of consent living in several systems, withdrawal propagating in seconds instead of on a nightly schedule, evidence recorded for every change including how it was captured, and a suppression check at send time rather than at list build. Most of these come apart on bidirectional sync. Letting two systems both write consent means one of them will eventually re-subscribe somebody who opted out, and that is a regulatory problem rather than a bug.
What matters here
- Give consent exactly one authoritative store. Two systems that both write it will eventually re-subscribe somebody.
- Propagate withdrawal in seconds. A nightly sync means somebody who unsubscribed on Monday is emailed on Tuesday.
- Record how consent was captured, not just that it exists. The evidence is what makes it defensible.
- Check suppression at send time, not at list build. A list built yesterday does not know about today withdrawal.
- Granular preferences beat a single flag. Somebody who wants product updates and not events should get exactly that.
Who this is for
You run marketing operations, privacy or revenue operations. Consent lives in three systems, they disagree, and nobody is certain which one an auditor would accept.
How it works in practice
What happens between somebody changing their mind and every system honouring it.
- 1
One store is authoritative for consent
Every other system reads from it and none of them write to it.
- 2
A change is captured with its evidence
Timestamp, source, IP or channel, the exact wording shown, and the version of it.
- 3
Withdrawal propagates within seconds
Push, not a nightly pull, because the gap between them is where the violation happens.
- 4
Preferences are granular, not one flag
Product updates, events, research and transactional, each held separately.
- 5
Suppression is checked at send time
A list built yesterday cannot know about a withdrawal this morning.
- 6
Every change is retained as evidence
Longer than the consent itself, because the question comes after the relationship ends.
What consent sync is made of
Four parts, and the first one prevents the regulatory failure.
One authoritative store
Consent has a single writer. Every other system reads. Bidirectional consent sync eventually re-subscribes somebody who opted out.
Immediate withdrawal
Pushed within seconds. A nightly reconciliation guarantees a window where a withdrawal is not yet honoured.
Recorded evidence
When, how, through which channel, and the exact wording shown. That the box was ticked is not evidence; what it said is.
Send-time suppression
Checked against the store at send, not at list build, because the list was correct yesterday and is not now.
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: choose one owner
This decision prevents the regulatory failure. Nothing else does.
Headless skills
build-workflowUse build-workflow. The systems in play are Marketo, Salesforce and our preference centre, or whatever we run in those seats. Show me every field in every system that currently expresses consent or subscription state. There will be more than expected and they will disagree. Then help me pick exactly one authoritative store. Everything else reads from it and nothing else writes to it. This is the whole design. Two systems that both write consent will eventually disagree, and the direction that hurts is the one that re-subscribes somebody who opted out.
Whichever store you choose, the preference centre should write to it directly rather than to a system that syncs to it. Every hop is a window where a withdrawal is not yet honoured.
- 2
Capture evidence, not only state
That the box was ticked is not evidence. What it said is.
Headless skills
build-workflowUse build-workflow. On every consent change record: Who, timestamp, and which preference changed Whether it was given or withdrawn The channel: preference centre, form, unsubscribe link, support agent, import The exact wording shown at the time, and its version IP address and user agent for a web capture, where lawful to keep Store the wording version, not a link to current wording. Consent text changes, and evidence that points at today's wording proves nothing about what somebody agreed to two years ago. Never overwrite a consent record. Append. The history is the evidence and the current state is a view over it.
- 3
Propagate withdrawal in seconds
The gap between systems is where the violation happens.
Headless skills
tray-patternsPush a withdrawal to every downstream system immediately, not on a schedule. Suppress in the marketing platform, the CRM, the sales engagement tool, any ad audience, and the support tool if it sends proactive email. Confirm each propagation and alert on any that fails instead of letting it retry quietly. A withdrawal that failed to reach one system is the one that sends the email. Grants can propagate on a normal schedule. Withdrawals cannot. That asymmetry is deliberate: the cost of a slow grant is a delayed newsletter and the cost of a slow withdrawal is a regulatory complaint.
- 4
Keep preferences granular and check at send
A list built yesterday does not know about this morning.
Headless skills
tray-gotchasUse tray-gotchas, then hold preferences granularly rather than as one flag: product updates, events, research, partner communications, and transactional separately. Somebody who wants release notes but not event invitations should get exactly that. A single subscribe flag forces people to choose all or nothing, and most choose nothing. Then check suppression at send time against the authoritative store, not at list build. A campaign list assembled yesterday does not know about a withdrawal this morning. Handle: transactional messages, which are not marketing and follow different rules, so classify them explicitly; a double opt-in that was never confirmed, which is not consent; and consent that has expired under a regime that time-limits it.
- 5
Reconcile, and keep the evidence longer than the consent
Because the question arrives after the relationship ends.
Reconcile daily between the authoritative store and every downstream system. Report anybody suppressed in one and not another, in both directions. The dangerous direction is somebody withdrawn centrally and still subscribed downstream. Alert on that immediately instead of reporting it. Retain the evidence longer than the consent itself. A complaint about an email arrives after somebody has left the database, and the record has to outlive the relationship to answer it. Report: consent by channel and preference, withdrawal rate, propagation failures, reconciliation differences, and how long a withdrawal takes to reach every system.
- 6
Prove it works, then hand it to privacy and marketing together
Because it is a compliance rule with a marketing consequence.
Run the per-step schema checks and the whole-workflow audit before this touches production. Test the withdrawal path end to end and measure how long it takes to reach the last system. Then open the same workflow in Tray Build so privacy and marketing operations can adjust preference categories and consent wording versions together in the visual canvas. Neither team should be able to change it alone.
What it connects to
One store writes, everything else reads, and withdrawal reaches all of them at once.
Marketo
Read consent from the authoritative store and suppress on it. Never the source of truth for consent itself.
Reads and writes
Salesforce
Display consent state read-only on the contact, so a rep can see it and cannot change it.
Writes
Outreach
Suppress immediately on withdrawal, because sales engagement tools are the most common source of a post-unsubscribe email.
Writes
Snowflake
Hold the append-only evidence log, retained longer than the consent itself.
Reads and writes
Slack
Alert on a propagation failure, because a withdrawal that did not land is the one that sends the email.
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, Jira, HubSpot or Databricks.
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 is a regulatory control. The failure mode is a complaint you cannot answer.
It runs where production runs, not on a laptop
Withdrawal propagation fires within seconds of a change, at any hour, with retries and confirmation per destination.
The evidence outlives the relationship
Append-only, with the wording version shown at capture. Evidence pointing at today wording proves nothing about a consent given two years ago.
No credential ever lands in the workflow itself
Consent write access is the most sensitive permission in the marketing stack. Held in your workspace, scoped, and never shared with a campaign tool.
Privacy and marketing own it together
Preference categories and wording versions open in Tray Build, and neither team should be able to change them alone.
Withdrawals never wait for a schedule
Grants can propagate on a nightly run. Withdrawals cannot, and that asymmetry is the entire point.
Questions people ask
Why can only one system own consent?
Because two systems that both write it will eventually disagree, and the direction that hurts is the one that re-subscribes somebody who opted out. That is a regulatory problem, not a data quality one.
Why must withdrawal propagate immediately?
Because a nightly sync guarantees a window in which somebody who unsubscribed has not yet been suppressed. Grants can wait; withdrawals cannot, and the asymmetry is deliberate.
What evidence should be recorded?
When, through which channel, and the exact wording shown at the time with its version. That a box was ticked is not evidence; what the box said is, and consent wording changes.
Why check suppression at send time?
Because a campaign list built yesterday does not know about a withdrawal this morning. Checking at list build is how a compliant process still sends the email.
Why granular preferences?
Because a single subscribe flag forces people to choose all or nothing, and most choose nothing. Somebody who wants release notes but not event invitations should be able to have exactly that.
Related guides
Legal and compliance
How to build data subject request automation
Search every system instead of the ones you remember, verify identity before disclosing, track the statutory clock, and record what was found. 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.
Last reviewed September 2026.