Beginner~12 min setupCRM & E-commerceVerified April 2026
HubSpot logo
Shopify logo

How to Sync Shopify Customers to HubSpot with Make

When a customer registers or places an order in Shopify, Make creates or updates their contact record in HubSpot with purchase history, tags, and contact details.

Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.

Best for

E-commerce brands running targeted email campaigns in HubSpot who need purchase data from Shopify available in their CRM within minutes of a transaction.

Not ideal for

Stores with 5,000+ daily orders — at that volume, a dedicated integration like Shopify's native HubSpot app handles batching and rate limits more reliably.

Sync type

real-time

Use case type

sync

Real-World Example

💡

A 12-person DTC skincare brand uses this to push every new Shopify customer into HubSpot the moment they check out, including order value and product tags. Before this automation, their marketing team exported a CSV from Shopify every Monday and manually imported it — contacts were up to 7 days stale by the time a campaign launched. Now HubSpot reflects the customer's latest order within 90 seconds, and their post-purchase email sequence enrolls automatically.

What Will This Cost?

Drag the slider to your expected monthly volume.

/mo
505005K50K

Each platform counts differently — Zapier: 1 task per trigger. Make: 1 operation per module per record. n8n: 1 execution per run.

Prices shown for annual billing. Based on published pricing as of April 2026.

Estimated ROI

1000

min saved/mo

$583

labor value/mo

Free

no platform cost

Based on ~2 min manual effort per operation at $35/hr fully loaded labor cost.

Implementation

Skip the setup

Import this workflow directly into Make

Copy the pre-built Make blueprint and paste it straight into Make. All modules, filters, and field mappings are already configured — you just need to connect your accounts.

Before You Start

Make sure you have everything ready.

Shopify Admin access with permission to create private apps and generate API tokens (requires 'read_orders' and 'read_customers' scopes)
HubSpot account with a user role that has 'Create contacts' and 'Edit contacts' permissions in CRM settings
Make account — Core plan or higher recommended for stores processing more than 300 orders/month (free tier allows ~1,000 ops/month)
Custom HubSpot contact properties pre-created for Shopify-specific fields like 'Last Order Value', 'Shopify Customer ID', and 'Total Orders Count' (create these in HubSpot Settings > Properties before building the scenario)
A Shopify test order ready to place during setup — Make needs a real webhook event to map the full data structure from Watch Orders

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Emailemail
First Namefirstname
Last Namelastname
Shopify Customer IDshopify_customer_id
Accepts Marketinghs_email_optout
7 optional fields▸ show
Phone Numberphone
Last Order Valuelast_order_value
Last Order Datelast_order_date
Total Orders Counttotal_orders_count
Citycity
Countrycountry
Customer Tagsshopify_customer_tags

Step-by-Step Setup

1

make.com > Scenarios > Create a new scenario

Create a new Make scenario

Log into Make at make.com and click the blue 'Create a new scenario' button in the top right of your Scenarios dashboard. You'll land on the visual canvas with a single empty module in the center. This is where you'll chain together Shopify and HubSpot modules. Give the scenario a name immediately — click the default 'New scenario' text at the top left and type something like 'Shopify → HubSpot Customer Sync' so it's findable later.

  1. 1Log in at make.com
  2. 2Click 'Create a new scenario' in the top right
  3. 3Click the pencil icon next to 'New scenario' at the top left
  4. 4Type a descriptive name like 'Shopify → HubSpot Customer Sync'
  5. 5Click the large '+' circle in the canvas center to add the first module
What you should see: You should see a blank canvas with one untitled circular module and your scenario name displayed at the top left.
Common mistake — Make auto-saves scenario names, but it does NOT auto-save module configuration. If your browser crashes mid-setup, you lose any unsaved module config. Click 'Save' (the floppy disk icon) after every completed module.
2

Canvas > + Module > Shopify > Watch Orders

Add the Shopify 'Watch Orders' trigger

Click the empty module circle to open the app picker. Search for 'Shopify' and select it. From the trigger list, choose 'Watch Orders' — this fires in real time via webhook whenever a new order is placed, which also creates or identifies a customer record. This is the right trigger because it captures both new customer registrations that come with a first purchase and repeat buyers whose details may have changed.

  1. 1Click the empty '+' module on the canvas
  2. 2Type 'Shopify' in the search bar
  3. 3Select 'Shopify' from the results
  4. 4Click 'Watch Orders' from the trigger list
  5. 5Click 'Add' to create a new Shopify connection
What you should see: A Shopify connection modal opens asking for your store URL and API credentials. The module title changes to 'Watch Orders'.
Common mistake — The 'Watch Orders' trigger only fires for orders created after the webhook is registered. It will NOT backfill historical orders. If you need to sync existing customers, run a separate one-time bulk import using 'Search Customers' before activating this scenario.
Make
+
click +
search apps
HubSpot
HU
HubSpot
Add the Shopify 'Watch Order…
HubSpot
HU
module added
3

Shopify Admin > Settings > Apps and sales channels > Develop apps > API credentials

Connect your Shopify store

In the connection modal, enter your Shopify store's subdomain (just the part before .myshopify.com), your API key, and your Admin API access token. You generate these in Shopify under Settings > Apps and sales channels > Develop apps. Make needs the 'read_orders' and 'read_customers' scopes at minimum. Name this connection something like 'Shopify - [Store Name]' so it's clear if you manage multiple stores.

  1. 1In Shopify Admin, go to Settings > Apps and sales channels
  2. 2Click 'Develop apps' then 'Create an app'
  3. 3Under 'Configuration', enable 'read_orders' and 'read_customers' API scopes
  4. 4Click 'Install app' and copy the Admin API access token
  5. 5Paste the store subdomain and access token into Make's connection modal
What you should see: Make displays a green checkmark and 'Connection successful' message. The Watch Orders module now shows your store name in the connection field.
Common mistake — Shopify only shows the Admin API access token once at creation. If you close that screen without copying it, you must regenerate a new token — there is no way to retrieve the original.
4

Watch Orders module > Settings panel

Configure the Watch Orders trigger settings

After connecting, Make asks which orders to watch. Set 'Order Status' to 'Any' so the trigger fires regardless of fulfillment state — you want the customer record the moment the order is placed, not after it ships. Set the 'Limit' field to 1 for real-time processing. Make will ask 'From which order do you want to start?' — choose 'From now on' unless you want to process a backlog, in which case pick a specific order ID.

  1. 1Set 'Order Status' to 'Any'
  2. 2Set 'Limit' to 1
  3. 3For the starting point selector, choose 'From now on'
  4. 4Click 'OK' to save the module configuration
What you should see: The Watch Orders module shows a clock/webhook icon indicating it's waiting for live events. You'll see 'Waiting for data' in the module footer.
5

Canvas > + after Watch Orders > Flow Control > Router

Add a Router to split new vs. existing customers

Click the '+' to the right of the Watch Orders module and add a 'Router' (found under Flow Control). The Router lets you send new customers down one path and returning customers down another — new customers need a HubSpot 'Create Contact' call, while existing ones need 'Update Contact'. Without this split, you'll create duplicate contacts every time a returning customer orders. You'll configure the filter conditions in the next step.

  1. 1Click the '+' arrow to the right of the Watch Orders module
  2. 2Click 'Flow Control' in the module picker
  3. 3Select 'Router'
  4. 4Confirm — two empty route branches appear on the canvas
What you should see: The canvas now shows the Watch Orders module connected to a Router diamond, with two empty branches extending to the right.
Common mistake — Make evaluates Router branches in top-to-bottom order and stops at the first matching filter. Put your most specific condition (existing customer check) as the first route to avoid new contacts accidentally falling into the update path.
6

Router Branch 1 > + Module > HubSpot CRM > Search for Contacts

Search HubSpot for an existing contact by email

On the first Router branch, add a HubSpot module: 'Search for Contacts'. Map the filter to search by the 'Email' property, using the value from Shopify's order output: {{1.customer.email}}. This query returns any existing HubSpot contact with that email. The result of this search determines which branch executes — if a contact is found, update it; if not, create it. Connect your HubSpot account using OAuth when prompted.

  1. 1Click the '+' on the first Router branch
  2. 2Search for 'HubSpot CRM' and select it
  3. 3Choose 'Search for Contacts'
  4. 4Connect HubSpot via OAuth — click 'Sign in with HubSpot' and authorize Make
  5. 5Set Filter Property to 'Email', Operator to 'Equal to', Value to {{1.customer.email}}
What you should see: The HubSpot Search for Contacts module appears on branch 1. When you run a test, it should return a contact bundle if the email exists in HubSpot, or an empty result if not.
Common mistake — HubSpot's contact search is case-insensitive for email, but Shopify sometimes passes emails in mixed case. Make's formula `{{lower(1.customer.email)}}` normalizes the email before the search and prevents near-duplicate matches.
7

Router > Click route connector wrench icon > Set filter condition

Add filters to each Router branch

Click the wrench icon on the Router connector between branch 1 and the next module to add a filter. For the 'Update' branch: set the condition to {{6.id}} (the HubSpot contact ID from the search) 'Exists'. For the 'Create' branch: set the condition to {{6.id}} 'Does not exist'. This ensures each order goes to exactly one path. Label each route clearly by clicking the route name — 'Existing Contact' and 'New Contact' keep the canvas readable.

  1. 1Click the wrench icon on the connector leaving the Router toward the update path
  2. 2Set Condition A to {{6.id}}, Operator to 'Exists'
  3. 3Click OK to save the filter
  4. 4Click the wrench icon on the second branch connector
  5. 5Set Condition A to {{6.id}}, Operator to 'Does not exist', click OK
What you should see: Each Router branch now shows a filter chip on its connector line. The canvas should read: Watch Orders → Router → [Existing Contact filter] and [New Contact filter].
Common mistake — Filters are the most common place setups break. Double-check the field name and value exactly match what your app sends — a single capital letter difference will block everything.
HubSpot
HU
trigger
filter
Condition
matches criteria?
yes — passes through
no — skipped
Shopify
SH
notified
8

New Contact Branch > + Module > HubSpot CRM > Create a Contact

Map fields and create new HubSpot contacts

On the 'New Contact' branch (where {{6.id}} does not exist), add a HubSpot CRM 'Create a Contact' module. Map the Shopify order fields to HubSpot contact properties. At minimum, map email, first name, last name, and phone. For purchase data, map total order value to a custom HubSpot property (you'll need to create this in HubSpot under Properties first). See the Field Mapping section below for the full recommended list.

  1. 1Click '+' on the 'New Contact' branch
  2. 2Select 'HubSpot CRM' > 'Create a Contact'
  3. 3In the Email field, map {{1.customer.email}}
  4. 4Map First Name to {{1.customer.first_name}}, Last Name to {{1.customer.last_name}}
  5. 5Map Phone to {{1.customer.phone}}, and Total Order Value to {{1.total_price}}
What you should see: The Create a Contact module shows green field mappings. Running a test with a real Shopify test order should create a new contact in HubSpot within 10 seconds.
Common mistake — HubSpot rejects contact creation if the email field is empty. Shopify guest checkouts can have a null email if the customer used Shop Pay with a phone number. Add a filter before this module: `{{1.customer.email}}` 'Exists' — otherwise the scenario errors on guest orders.
HubSpot fields
firstname
lastname
email
company
hs_lead_status
available as variables:
1.props.firstname
1.props.lastname
1.props.email
1.props.company
1.props.hs_lead_status
9

Existing Contact Branch > + Module > HubSpot CRM > Update a Contact

Map fields and update existing HubSpot contacts

On the 'Existing Contact' branch, add a HubSpot CRM 'Update a Contact' module. Set the Contact ID field to {{6.id}} — the ID returned by your earlier search. Map the same fields as the create step, plus any fields that change over time like phone number or marketing opt-in status. HubSpot's update call is a patch, so unmapped fields are left untouched — you don't need to re-send every property.

  1. 1Click '+' on the 'Existing Contact' branch
  2. 2Select 'HubSpot CRM' > 'Update a Contact'
  3. 3Set 'Contact ID' to {{6.id}}
  4. 4Map Email, Phone, and Last Order Value fields
  5. 5Map {{1.total_price}} to your custom 'Last Order Value' HubSpot property
What you should see: The Update a Contact module is connected on the existing-contact branch. A test run against a known email in HubSpot should show the contact's properties updated without creating a duplicate.
10

Canvas > Run once button (bottom toolbar) > Execution log

Test the full scenario end to end

Click 'Run once' in the bottom toolbar. Then place a test order in your Shopify store using a test customer email. Make's execution log will show each module lighting up green (success) or red (error). Click any module bubble in the log to inspect the exact data that passed through it. Check HubSpot manually to confirm the contact was created or updated with the correct field values. Run the test twice — once with a new email and once with an existing HubSpot contact email — to verify both Router branches work.

  1. 1Click 'Run once' in the bottom toolbar
  2. 2Place a test order in Shopify with a brand-new email address
  3. 3Watch the Make canvas — modules flash as they execute
  4. 4Click each module bubble to inspect input/output data
  5. 5Repeat with an email already in HubSpot to test the update branch
What you should see: Both branches execute successfully. New email creates a fresh contact in HubSpot; existing email updates the existing record. The execution log shows all modules with green checkmarks and zero errors.
Common mistake — Shopify test orders placed through the 'Bogus Gateway' payment method fire real webhooks. But Shopify's 'Draft Orders' do NOT trigger the Watch Orders webhook until they're converted to actual orders — don't use draft orders for testing.
Make
▶ Run once
executed
HubSpot
Shopify
Shopify
🔔 notification
received
11

Canvas > Toggle switch (bottom left) > ON | Scenario Settings > Advanced

Activate the scenario and set scheduling

Once both test branches pass, click the toggle switch in the bottom left of the canvas from OFF to ON. Make will ask you to confirm activation. Because this scenario uses a webhook trigger (Watch Orders), it runs instantly on each Shopify order — you don't set a polling interval. However, you should set a scenario timeout under Settings > Advanced to 300 seconds so a slow HubSpot API response doesn't leave executions hanging. Also enable 'Auto-commit' in scenario settings so partial failures don't roll back successful modules.

  1. 1Click the OFF/ON toggle in the bottom left of the canvas
  2. 2Confirm activation in the dialog
  3. 3Click the gear icon (Scenario Settings) in the bottom toolbar
  4. 4Set 'Max number of cycles' to 1
  5. 5Set 'Sequential processing' to ON to prevent race conditions on rapid repeat orders
What you should see: The toggle shows blue/ON. The scenario status on your dashboard changes to 'Active'. The next real Shopify order will trigger it automatically within 60–90 seconds.
Common mistake — Make free plans cap scenarios at 1,000 operations/month. Each run of this scenario uses 3–4 operations (Watch Orders + Search + Create or Update). At 300 orders/month that's ~1,200 operations — you'll need at least the Core plan ($10.59/month) or you'll hit the free tier ceiling mid-month.

Paste this formula into Make's 'Phone' field mapping to handle Shopify's inconsistent phone data and normalize the accepts_marketing flag to HubSpot's expected boolean format. Use it directly in the field value box of the HubSpot Create or Update Contact module — no separate module needed.

JavaScript — Custom Function{{-- Phone fallback: use customer phone if present, otherwise use billing address phone --}}
▸ Show code
{{-- Phone fallback: use customer phone if present, otherwise use billing address phone --}}
{{if(
  and(1.customer.phone; length(trim(1.customer.phone)) > 0);

... expand to see full code

{{-- Phone fallback: use customer phone if present, otherwise use billing address phone --}}
{{if(
  and(1.customer.phone; length(trim(1.customer.phone)) > 0);
  trim(1.customer.phone);
  trim(1.billing_address.phone)
)}}

{{-- Email normalization for dedup search --}}
{{lower(trim(1.customer.email))}}

{{-- Convert Shopify total_price string to HubSpot number property --}}
{{toNumber(1.total_price)}}

{{-- Map accepts_marketing to HubSpot opt-out (inverted logic) --}}
{{-- HubSpot hs_email_optout = true means OPTED OUT --}}
{{if(1.customer.accepts_marketing; false; true)}}

{{-- Format Shopify ISO timestamp to HubSpot date format (YYYY-MM-DD) --}}
{{formatDate(1.created_at; "YYYY-MM-DD"; "America/New_York")}}

{{-- Build a readable customer tag string from Shopify array --}}
{{join(1.customer.tags; ", ")}}

Going live

Production Checklist

Before you turn this on for real, confirm each item.

Troubleshooting

Common errors and how to fix them.

Frequently Asked Questions

Common questions about this workflow.

Analysis

VerdictWhy Make for this workflow

Use Make for this if your team thinks visually, you're running a small-to-mid-size Shopify store (under 3,000 orders/month), and you need conditional logic — specifically the Router branch that separates new contact creation from updates. Make's canvas makes that deduplication logic easy to build and debug without writing code. The one scenario where you'd pick something else: if you're already on HubSpot Operations Hub Professional, their native Shopify integration handles this sync with zero setup and better error recovery than anything you'll build on Make.

Cost

Real cost math: each scenario run on Make costs 3–4 operations (Watch Orders = 1, Search Contacts = 1, Create or Update = 1, plus 1 for the Router). At 500 orders/month that's ~2,000 operations. Make's free tier allows 1,000 operations/month, so you'll exceed it. The Core plan at $10.59/month includes 10,000 operations — enough headroom for ~2,500 orders/month. Zapier's equivalent setup costs $19.99/month minimum (Starter plan) and counts each step as a task, so 500 orders × 3 steps = 1,500 tasks, which fits the Starter tier — but barely. Make is cheaper here by $9.40/month for mid-volume stores.

Tradeoffs

Honest competitor comparison: Zapier's Shopify 'New Order' trigger is faster to configure and fires more reliably (Shopify is a Zapier Premier Partner with optimized webhooks), but Zapier has no native Router — you'd need multi-step Zaps with filters and Pay-per-task pricing adds up fast. n8n handles this more elegantly in code with a single JavaScript node that does the search and upsert in one call, and it's free self-hosted, but someone on your team needs to maintain the instance. Power Automate has a Shopify connector in preview that's unreliable — Microsoft hasn't prioritized it. Pipedream lets you write the entire sync in 25 lines of JavaScript with native retry and built-in HTTP request logging, which is genuinely better for developers who want full control. Make wins for non-technical operators who need to maintain this themselves six months from now.

Three things you'll hit after go-live: First, Shopify's 'total_price' includes taxes and shipping — if your HubSpot contact property is meant to track product revenue only, you need to map subtotal_price instead, which most people miss on first setup. Second, Make scenarios occasionally miss a webhook if the scenario is in an error state when Shopify fires — Make does NOT retry missed webhooks by default. Enable the 'Incomplete executions' setting in scenario settings, which queues failed runs for manual replay. Third, HubSpot's Search for Contacts module has a bug where it returns an empty bundle (not null) when no contact is found — this means your {{6.id}} Does not exist filter may not evaluate as expected if you built the condition assuming a true null. Test this explicitly with an email you know isn't in HubSpot.

Ideas for what to build next

  • Sync HubSpot Contact Updates Back to ShopifyWhen a sales rep updates a phone number or address in HubSpot, that change currently doesn't reflect in Shopify. Build a reverse scenario using HubSpot's 'Watch Contacts' trigger to push updates back to Shopify's Customer API and keep both systems consistent.
  • Enroll Synced Contacts in HubSpot Post-Purchase SequencesAdd a HubSpot 'Add Contact to List' or 'Enroll in Workflow' module at the end of this scenario so new Shopify buyers immediately enter a post-purchase email sequence without any manual list management.
  • Add Slack Alerts for High-Value OrdersExtend this scenario with a filter after Watch Orders: if total_price exceeds a threshold (e.g. $500), route to a Slack 'Create a Message' module that posts the order details to your #sales channel so the team can follow up personally.

Related guides

Was this guide helpful?
HubSpot + Shopify overviewMake profile →