A daily batch sync from a lead CRM into a sales CRM, with a ledger
Once a day, a Make scenario reads new and changed contacts and opportunities from the lead CRM and upserts them into the sales CRM in batches, contacts first and then deals, recording what it synced in a data store. A real-time twin applies the same steps to single events.
Where this applies
Any business that captures leads in one CRM and sells or reports from another: agencies, contractors and franchises running a marketing CRM beside a sales CRM.
- daily runs, none failed
- 34
- credits a run, on average
- 163
- scenarios: daily batch and real-time
- 2
The problem
Many businesses capture leads in one CRM, usually the one their calls, texts and forms run through, and run sales and reporting from another. Unless the two are joined, the sales side works from a partial picture and every report starts with an export.
Copying records one at a time through each system's API looks simple and does not scale: a call per contact and another per deal soon runs into rate limits and spends credits, and a sync that only creates will duplicate anyone it sees twice.
What we built
Read from the source, write in batches
The scenario sets its variables, then makes three reads against the lead CRM's API for contacts and opportunities. Instead of writing each record as it arrives, an iterator turns every record into a JSON object and a text aggregator joins them into one batch body, sent to the sales CRM's batch upsert endpoint in a single request.
Contacts first, then deals
A second iterator, JSON builder and aggregator build the deal batch the same way, so deals are written after the contacts they belong to. A last iterator makes the per-record calls that remain once the two batches are in.
Upsert, not create
Both batches go to upsert endpoints, which update a record that already exists and create one that does not. A contact who changes on two different days is written twice to the same record rather than duplicated.
A ledger in a data store
Each synced record is written to a data store with add-or-replace, so there is one row per record and a place to see what crossed and when without opening either CRM.
Daily, with a real-time twin
The batch runs once a day in the afternoon, when its cost is predictable and its batches are full. A second scenario takes the lead CRM's webhook and runs the same read, build and batch steps for a single event, for the cases where the next day is too late.
The outcome
The daily sync ran 34 times between 18 September and 21 October 2026, every run successful, using 5,542 credits: about 163 a run for the whole day's contacts and deals.
The real-time twin is switched on but has not fired in the current billing period, so the daily batch does the work.
Where it stops
The sync runs one way, from the lead CRM into the sales CRM; an edit made on the sales side is not sent back. Almost every external call has an ignore handler, so a call that fails is skipped without an alert, and the ledger, not an error, is where a gap would show. The real-time twin has no traffic yet, so only the daily batch has a run history.
Built with
- Make.com
- GoHighLevel API
- HubSpot API
- HTTP
- Make data store
Patterns used
- Batch body from a text aggregator
- Upsert, not create
- Data store as sync ledger
- Daily batch with a real-time twin
Page copy drafted with AI assistance. Skofi is accountable for it. Editorial policy
Have something shaped like this?
If any part of the above resembles a problem you are living with, a short call is the fastest way to find out whether it is the same problem underneath.
What happens on the call
- 15 minutes, no deck
- We map the process on the call
- A written scope, or an honest no