Skip to content

Integration  ·  Revenue operations

How to build a CRM to marketing automation sync

A rep updates a job title, the marketing platform overwrites it that night, and the rep updates it again. Below is the model behind a sync that settles, the prompts that build it, and the parts that only bite once it is live.

Built with Tray Headless

  1. System Salesforce
  2. Step Delta since watermark
  3. Step Ownership table
  4. Step Loop breaker
  5. System Marketo
Also Conflict surfaced

The ownership table decides every field, and the loop breaker stops a change echoing between the two systems.

The short answer

What is a CRM to marketing automation sync?

Done well, a CRM to marketing automation sync is four things: field ownership decided per field before anything is built, delta syncing instead of full passes, an explicit reconciliation of the two object models, and loop prevention so an update does not bounce between systems forever. Teams usually come unstuck on ownership. Without a field-level decision about which system wins, the two platforms overwrite each other nightly and both datasets become untrustworthy.

What matters here

  • Decide ownership per field, in writing, before building. Without it the two systems overwrite each other and nobody trusts either.
  • Sync deltas, not everything. A full pass every night burns API limits and hides the change that mattered.
  • The object models do not match. A CRM lead and contact are one person in the marketing platform, and pretending otherwise creates duplicates.
  • Break loops with an update-source marker. Otherwise a change bounces between systems and consumes the API limit in a day.
  • Never sync opt-out state in both directions with equal authority. Consent is a legal record and it needs one owner.

Who this is for

You run marketing operations or revenue operations. Two platforms hold overlapping data about the same people, the sync was configured by somebody who has left, and both teams distrust the other system.

How it works in practice

Everything that sits between a field changing in one system and settling in both.

  1. 1

    Ownership is decided per field, up front

    Sales owns title and phone, marketing owns lead score and campaign history, and both are written down before anything runs.

  2. 2

    Only changed records are synced

    A delta against the last successful run, so the volume matches what actually happened instead of the size of the database.

  3. 3

    Lead and contact are reconciled to one person

    The CRM splits a person across two objects, the marketing platform does not, and the mapping has to be explicit.

  4. 4

    Each write carries an update-source marker

    So the receiving system knows the change came from the sync and does not echo it back.

  5. 5

    Consent flows one way only

    Opt-out has a single authoritative owner, because it is a legal record instead of a preference field.

  6. 6

    Conflicts are surfaced, not silently resolved

    When both sides changed the same field since the last run, somebody is told rather than the newer timestamp winning by default.

What the sync is made of

Two systems that overlap by design. Four parts, and the first one is a decision rather than code.

Field-level ownership

A written table of which system wins per field. This is the whole design, and skipping it is why most of these syncs are distrusted.

Delta syncing

Changed records since the last watermark, not a full pass. API limits are finite and a full sync hides the meaningful change in noise.

An object model reconciliation

Lead and contact map to one person. Handle the conversion event explicitly or you create a duplicate at the exact moment the deal gets real.

Loop prevention

An update-source marker on every write so a change does not echo between systems until the API limit runs out.

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. 1

    Set up, then write the ownership table first

    This is the design. Everything else is plumbing.

    Headless skills build-workflow

    Use build-workflow. The systems in play are Salesforce and Marketo, or
    whatever we run in those seats.
    
    Before building anything, produce a field ownership table: every field
    that exists in both systems, which one wins, and whether it syncs one way
    or both.
    
    My starting position: sales owns name, title, phone and account
    relationship. Marketing owns lead score, campaign membership, email
    engagement and lifecycle stage. Consent and opt-out are marketing-owned
    and one-way only.
    
    Show me the fields that are in both systems and not on that list. Those
    are the ones that will cause arguments, and they need deciding now rather
    than at 2am when the sync loops.
  2. 2

    Sync deltas, with a watermark you can trust

    A full nightly pass burns the API limit and hides the real change.

    Headless skills build-workflow tray-patterns

    Use build-workflow. Sync changed records only, against a watermark stored
    per direction and per object.
    
    Take a small overlap on the watermark, a few minutes, so a record modified
    during the previous run is not skipped. Deduplicate on the receiving side
    rather than risking a gap.
    
    Batch the writes. Both platforms have API limits and both will throttle
    you, so handle a 429 with backoff rather than failing the run.
    
    Record the watermark only after a successful run. A partial failure that
    advances the watermark silently drops every record in the gap, and
    nobody finds out until somebody notices a person missing from a
    campaign.
  3. 3

    Reconcile the object models explicitly

    Where duplicates are created, usually at conversion.

    The models do not match. A person is one record in Marketo and may be a
    Lead or a Contact in Salesforce, and the conversion between them is the
    dangerous moment.
    
    Handle these:
    
      Lead to contact conversion. The Marketo person must follow the
      conversion, keeping its history, not become a second person attached to
      the new contact.
    
      A person who exists in Marketo with no Salesforce record. Decide
      whether that syncs across at all, and under what condition.
    
      The same email on both a lead and a contact. That is a CRM
      deduplication problem surfacing here, and this sync should report it
      instead of pick one.
    
    Key the mapping on the Marketo id stored in Salesforce and the Salesforce
    id stored in Marketo, not on email. People change email addresses and
    that breaks an email-keyed sync silently.

    Conversion is the moment a deal becomes real and the moment this sync is most likely to duplicate the person. Test it first, not last.

  4. 4

    Break the loops before they start

    Otherwise one change consumes the day API limit by lunchtime.

    Headless skills tray-gotchas

    Use tray-gotchas, then prevent echo.
    
    Stamp every write with an update-source marker. When reading changes,
    skip anything whose last modification carries this sync as the source.
    
    Then add a circuit breaker: if the same record syncs more than a handful
    of times in an hour, stop syncing it, flag it, and alert. That pattern is
    always a loop or a fighting pair of rules, and it will exhaust the API
    limit before anybody notices.
    
    Also handle a genuine conflict: both systems changed the same field since
    the last run. Do not resolve it on timestamp. Apply the ownership table,
    and if the field is owned by neither side conclusively, hold it and
    surface it.
  5. 5

    Treat consent as a legal record

    The one field where a sync mistake is a regulatory problem.

    Opt-out and consent sync one way only, from whichever system your
    privacy policy names as authoritative. Usually the marketing platform.
    
    Never let a CRM update re-subscribe somebody. A rep changing a field
    should not be able to undo an unsubscribe, and a bidirectional sync makes
    that possible by accident.
    
    Record every consent change with its source, timestamp and the run that
    carried it. When somebody asks why they received an email after
    unsubscribing, that record is the answer.
    
    Then report weekly: records synced per direction, conflicts surfaced,
    loop breaker trips, API usage against limit, and any consent change that
    did not originate in the authoritative system. That last one should
    always be zero.
  6. 6

    Check it end to end, then hand the ownership table over

    Because field ownership is a negotiation between two teams.

    Run the per-step schema checks and the whole-workflow audit before this
    touches production. Run in report-only mode for a week first: compute
    what would change, write nothing, and read the diff with both teams.
    
    Then open the same workflow in Tray Build so marketing and revenue
    operations can amend the ownership table together in the visual canvas.
    That table is a negotiation between two teams, and it should not need a
    third to change it.

What it connects to

Two platforms holding the same people, and the places a conflict needs to surface.

Salesforce

Read and write leads and contacts, respecting the ownership table field by field rather than object by object.

Reads and writes

Marketo

Read and write the person record, campaign membership and lifecycle stage, with consent flowing one way only.

Reads and writes

Slack

Surface conflicts and loop breaker trips to both operations teams, because neither will read a log.

Writes

Snowflake

Land the sync history and every conflict, so ownership disputes are settled with data instead of recollection.

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

Two teams depend on this and both will blame it first. It needs to be observable.

It lives on the platform, not in a terminal window

Delta syncing in both directions, with backoff against two sets of API limits, executes on the same engine with a watermark that survives a failure.

Every write is attributable to a direction

Which system originated a change, and which run carried it. That is how an ownership dispute gets settled in a minute, not a meeting.

Credentials are held by the platform, never hardcoded

Two platform credentials, each scoped to the objects in the ownership table and nothing wider, held in your workspace.

Both teams own the table

Field ownership opens in Tray Build so marketing and revenue operations amend it together, which is the only way it stays current.

The loop breaker is not optional

A record syncing repeatedly in an hour stops and alerts. Without it a single rule conflict exhausts the API limit before lunchtime.

Questions people ask

Why decide field ownership before building?

Because without it the two systems overwrite each other nightly. A rep corrects a title, the platform reverts it, and within a month neither team believes the other system.

Why not sync everything every night?

Because a full pass burns API limits in both platforms and buries the change that mattered in tens of thousands that did not. Deltas against a watermark match the volume to what actually happened.

Where do duplicates come from?

Usually lead to contact conversion. The CRM splits a person across two objects and the marketing platform does not, so unless conversion is handled explicitly the person becomes two people at the moment the deal gets real.

How do I stop a sync loop?

Stamp every write with an update-source marker and skip changes carrying it. Add a circuit breaker that stops any record syncing repeatedly within an hour, because that is always a loop and it will exhaust the API limit.

Should consent sync both ways?

No. Opt-out is a legal record and needs one authoritative owner. A bidirectional sync makes it possible for a CRM edit to re-subscribe somebody who unsubscribed, by accident.

Further reading

Background on the same subject, for the case rather than the build.

Last reviewed September 2026.