Intermediate~15 min setupCRM & E-commerceVerified April 2026
HubSpot logo
Shopify logo

How to Sync Shopify Customers to HubSpot with Pipedream

Instantly creates or updates a HubSpot contact every time a new customer registers or places an order in Shopify, keeping purchase history and contact details current.

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

Best for

E-commerce teams that want real-time CRM updates without waiting for a nightly CSV import or a third-party connector subscription.

Not ideal for

Stores with 50,000+ orders per month — at that volume, batch processing with the Shopify bulk API and a scheduled workflow will be more reliable than per-event webhooks.

Sync type

real-time

Use case type

sync

Real-World Example

💡

A 12-person DTC skincare brand was uploading a Shopify customer CSV into HubSpot every Friday morning. Marketing campaigns were targeting week-old data, and first-purchase welcome flows were firing 5-7 days late. After this workflow, every new Shopify order creates or updates a HubSpot contact within 30 seconds, and the team's welcome sequence now starts the same day a customer buys.

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 Pipedream

Copy the pre-built Pipedream blueprint and paste it straight into Pipedream. 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 store with Admin API access — you need the 'read_orders' and 'read_customers' scopes enabled for the connected account
HubSpot account with a Private App token or OAuth connection that has 'crm.objects.contacts.read' and 'crm.objects.contacts.write' scopes
A custom HubSpot contact property named 'shopify_customer_id' (type: single-line text) created before running the workflow — used for deduplication
Pipedream account — free tier supports up to 10,000 credits/month; this workflow uses roughly 3-4 credits per order event

Optional

At least one completed Shopify order in the store to use as a test event during setup

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Email Addressemail
First Namefirstname
Last Namelastname
Shopify Customer IDshopify_customer_id
6 optional fields▸ show
Phone Numberphone
Citycity
Country Codecountry
Total Spenttotal_revenue
Orders Countshopify_orders_count
Last Order Dateshopify_last_order_date

Step-by-Step Setup

1

pipedream.com > Workflows > New Workflow

Create a new Pipedream workflow

Go to pipedream.com and sign in. Click 'New Workflow' in the top-right corner of the dashboard. You'll land on the workflow builder canvas with an empty trigger slot at the top. Give the workflow a name — something like 'Shopify → HubSpot Customer Sync' — using the pencil icon at the top of the page so you can find it later.

  1. 1Click 'New Workflow' in the top-right of the Pipedream dashboard
  2. 2Click the pencil icon next to 'My Workflow' and rename it to 'Shopify → HubSpot Customer Sync'
  3. 3Click 'Save' to confirm the name
What you should see: You should see an empty workflow canvas with a grey trigger block at the top labeled 'Add a trigger'.
2

Workflow Canvas > Add a trigger > Shopify > New Order (Instant)

Add a Shopify trigger for new orders

Click the 'Add a trigger' block. Search for 'Shopify' in the app search box and select it. From the list of available triggers, choose 'New Order (Instant)' — this fires via webhook the moment Shopify creates an order, not on a delay. Pipedream will ask you to connect your Shopify store; click 'Connect Shopify Account' and enter your store's myshopify.com subdomain.

  1. 1Click the grey 'Add a trigger' block on the canvas
  2. 2Type 'Shopify' in the search field and click the Shopify app icon
  3. 3Select 'New Order (Instant)' from the trigger list
  4. 4Click 'Connect Shopify Account' and enter your store subdomain (e.g. mystore.myshopify.com)
  5. 5Click 'Save and continue'
What you should see: You should see a green 'Connected' badge next to your Shopify store name, and the trigger block will show 'Listening for events...'.
Common mistake — Use 'New Order (Instant)' not 'New Order (Polling)'. The polling version checks every 15 minutes — the instant webhook version fires in under 30 seconds. They are separate items in the trigger list.
Pipedream
+
click +
search apps
HubSpot
HU
HubSpot
Add a Shopify trigger for ne…
HubSpot
HU
module added
3

Trigger Block > Generate Test Event

Test the trigger with a real Shopify order

Click 'Generate Test Event' at the bottom of the trigger configuration panel. Pipedream will pull in the most recent order from your Shopify store as sample data. If your store has no orders yet, place a test order using Shopify's built-in test payment gateway first. Confirm that the event payload in the right panel contains customer fields like email, first_name, last_name, and phone.

  1. 1Click 'Generate Test Event' in the trigger configuration panel
  2. 2Wait for Pipedream to fetch the most recent Shopify order
  3. 3Expand the 'customer' object in the event payload on the right panel
  4. 4Confirm you can see fields: email, first_name, last_name, phone, total_spent
What you should see: The right panel displays a full JSON payload. Expanding 'customer' shows real customer data including email address and billing address fields.
Common mistake — Shopify's test orders use a fake email like '[email protected]'. If you're using Shopify's Bogus Gateway for testing, replace the test event with a real order before going live — otherwise you'll create junk contacts in HubSpot.
Pipedream
▶ Deploy & test
executed
HubSpot
Shopify
Shopify
🔔 notification
received
4

Workflow Canvas > + > Run Node.js code

Add a Node.js code step to extract and clean customer data

Click the '+' button below the trigger block to add a step. Select 'Run Node.js code'. This step pulls the customer fields out of the Shopify order payload and normalizes them — lowercasing the email, combining address fields — so HubSpot receives clean data. Paste the code from the Pro Tip section below directly into the code editor. This runs before any HubSpot API call, so errors here won't create bad contacts.

  1. 1Click the '+' icon below the trigger block
  2. 2Select 'Run Node.js code' from the step options
  3. 3Clear the default code in the editor
  4. 4Paste the transformation code from the Pro Tip section
  5. 5Click 'Test' to run the step against your test event
What you should see: The step returns a 'Return Value' panel on the right showing a clean object with fields like email, firstname, lastname, phone, and address properties ready for HubSpot.
Common mistake — Shopify orders can arrive without a customer object if the buyer checked out as a guest with no account. Add a guard clause at the top of your code: if (!event.customer) return $.flow.exit('Guest checkout — no customer record'). Without this, the workflow will throw a null reference error on guest orders.

Paste this into the Node.js code step (Step 4) that sits between your Shopify trigger and the HubSpot Find Contact step. It extracts and normalizes the customer fields, handles guest checkouts gracefully, and exports a clean object that all downstream HubSpot steps can reference via steps.normalize_customer.$return_value.

JavaScript — Code Step// Step: normalize_customer
▸ Show code
// Step: normalize_customer
// Runs after Shopify 'New Order (Instant)' trigger
// Cleans and extracts customer data before HubSpot API calls

... expand to see full code

// Step: normalize_customer
// Runs after Shopify 'New Order (Instant)' trigger
// Cleans and extracts customer data before HubSpot API calls

export default defineComponent({
  async run({ steps, $ }) {
    const order = steps.trigger.event;
    const customer = order.customer;

    // Exit cleanly on guest checkouts — no customer record to sync
    if (!customer || !customer.email) {
      return $.flow.exit('Skipping: guest checkout with no customer record');
    }

    const address = customer.default_address || order.billing_address || {};

    // Normalize the data for HubSpot
    const normalized = {
      email: customer.email.toLowerCase().trim(),
      firstname: customer.first_name?.trim() || '',
      lastname: customer.last_name?.trim() || '',
      phone: customer.phone || address.phone || null,
      city: address.city || null,
      country: address.country_code || null,
      total_revenue: customer.total_spent || '0.00',
      shopify_customer_id: String(customer.id),
      shopify_orders_count: String(customer.orders_count || 1),
      shopify_last_order_date: order.created_at || new Date().toISOString(),
    };

    // Validate required fields before proceeding
    if (!normalized.email.includes('@')) {
      throw new Error(`Invalid email format: ${normalized.email}`);
    }

    console.log(`[Normalize] Customer ${normalized.email} | Orders: ${normalized.shopify_orders_count} | Spent: $${normalized.total_revenue}`);

    return normalized;
  }
});
5

Workflow Canvas > + > HubSpot > Find Contact

Add a HubSpot step to search for an existing contact

Click '+' below the code step. Search for 'HubSpot' and select it. Choose the action 'Search CRM' or specifically 'Find Contact'. Set the search property to 'email' and map the value to the email output from your previous code step using the data picker (click the '{' icon to browse available fields). This check prevents duplicate contacts from being created when the same customer places a second order.

  1. 1Click the '+' icon below the Node.js code step
  2. 2Search for 'HubSpot' and click the HubSpot app icon
  3. 3Select 'Find Contact' from the action list
  4. 4Connect your HubSpot account via 'Connect HubSpot Account'
  5. 5Set Property Name to 'email' and click the '{' icon to map Property Value to steps.nodejs.$return_value.email
What you should see: After clicking 'Test', the step returns either a contact object with an 'id' field if a match exists, or an empty result if no match is found.
6

Workflow Canvas > + > Run Node.js code

Add a conditional branch to route new vs. existing contacts

Click '+' below the HubSpot search step. Select 'Filter' or use a second Node.js code step to check whether the previous step returned a contact ID. If a contact ID exists, the workflow should update that contact. If no ID exists, it should create a new one. Pipedream supports this with a simple if/else in a code step — check steps.find_contact.$return_value.id and set a variable like contactId and isExisting accordingly.

  1. 1Click '+' below the Find Contact step
  2. 2Select 'Run Node.js code'
  3. 3Write a conditional that checks if steps.find_contact.$return_value?.id exists
  4. 4Export contactId and isExisting as return values for the next step
What you should see: The step's Return Value panel shows either { contactId: '12345', isExisting: true } or { contactId: null, isExisting: false } depending on the test data.
Common mistake — HubSpot's 'Find Contact' action returns an array in some API versions. Check if the result is an array and access index [0] — using result.id directly without checking will throw undefined errors when HubSpot returns a list wrapper instead of a bare object.
HubSpot
HU
trigger
filter
Condition
= "New"
yes — passes through
no — skipped
Shopify
SH
notified
7

Workflow Canvas > + > HubSpot > Create Contact

Add a HubSpot step to create a new contact

Click '+' and add another HubSpot step. Select 'Create Contact'. Map each property from your Node.js clean-data step: email → email, first_name → firstname, last_name → lastname, phone → phone, city → city, country → country_code. Use the '{' data picker to reference steps.nodejs.$return_value for each field. This step only needs to run when isExisting is false — you'll handle that with the condition check in the next step or via Pipedream's step-level configuration.

  1. 1Click '+' and select HubSpot > Create Contact
  2. 2Click '{' next to Email and select steps.nodejs.$return_value.email
  3. 3Map firstname, lastname, phone, city, and country_code from the same source
  4. 4Add a custom property 'shopify_customer_id' and map it to steps.trigger.event.customer.id
  5. 5Click 'Test' to verify the contact is created in HubSpot sandbox
What you should see: HubSpot returns a 201 response and the step shows the newly created contact's ID in the Return Value panel. You can verify in HubSpot under Contacts > All Contacts.
Common mistake — HubSpot's API rejects contacts if the email domain is a known disposable mail provider (e.g. mailinator.com). This won't throw a 4xx in all cases — sometimes it silently drops the record. Test with a real business email address, not a throwaway.
8

Workflow Canvas > + > HubSpot > Update Contact

Add a HubSpot step to update an existing contact

Click '+' after your create step and add another HubSpot action. Select 'Update Contact'. Set the Contact ID field to the contactId value exported from your conditional step. Map the same field set as the create step. This ensures that when a returning customer places a new order, their phone number, address, and any other updated details are written back to HubSpot immediately.

  1. 1Click '+' and select HubSpot > Update Contact
  2. 2Set Contact ID to steps.conditional.$return_value.contactId
  3. 3Map firstname, lastname, phone, city, and country_code from steps.nodejs.$return_value
  4. 4Add the field total_spent and map it to steps.trigger.event.customer.total_spent
  5. 5Click 'Test' using a test event with an existing customer email
What you should see: HubSpot returns a 200 response. Opening the contact in HubSpot shows the updated fields and a 'Last Modified' timestamp matching the test run time.
9

Workflow Canvas > + > Run Node.js code

Add a final code step to log the outcome

Add one more Node.js step at the end of the workflow. This step logs whether a contact was created or updated, the HubSpot contact ID, and the Shopify order ID. Use console.log() — Pipedream captures all logs under the 'Logs' tab for each workflow run. This makes debugging failed runs significantly faster because you can see exactly which Shopify order triggered what HubSpot action without digging through raw JSON.

  1. 1Click '+' and select 'Run Node.js code'
  2. 2Write a console.log statement outputting the Shopify order ID, HubSpot contact ID, and action taken (created vs updated)
  3. 3Return a summary object with the same fields
  4. 4Click 'Test' and open the 'Logs' tab to confirm the output appears
What you should see: The Logs tab shows a line like: '[Sync] Order #4521 → HubSpot Contact 12345 (updated)' with no errors in the step output.

Paste this into the Node.js code step (Step 4) that sits between your Shopify trigger and the HubSpot Find Contact step. It extracts and normalizes the customer fields, handles guest checkouts gracefully, and exports a clean object that all downstream HubSpot steps can reference via steps.normalize_customer.$return_value.

JavaScript — Code Step// Step: normalize_customer
▸ Show code
// Step: normalize_customer
// Runs after Shopify 'New Order (Instant)' trigger
// Cleans and extracts customer data before HubSpot API calls

... expand to see full code

// Step: normalize_customer
// Runs after Shopify 'New Order (Instant)' trigger
// Cleans and extracts customer data before HubSpot API calls

export default defineComponent({
  async run({ steps, $ }) {
    const order = steps.trigger.event;
    const customer = order.customer;

    // Exit cleanly on guest checkouts — no customer record to sync
    if (!customer || !customer.email) {
      return $.flow.exit('Skipping: guest checkout with no customer record');
    }

    const address = customer.default_address || order.billing_address || {};

    // Normalize the data for HubSpot
    const normalized = {
      email: customer.email.toLowerCase().trim(),
      firstname: customer.first_name?.trim() || '',
      lastname: customer.last_name?.trim() || '',
      phone: customer.phone || address.phone || null,
      city: address.city || null,
      country: address.country_code || null,
      total_revenue: customer.total_spent || '0.00',
      shopify_customer_id: String(customer.id),
      shopify_orders_count: String(customer.orders_count || 1),
      shopify_last_order_date: order.created_at || new Date().toISOString(),
    };

    // Validate required fields before proceeding
    if (!normalized.email.includes('@')) {
      throw new Error(`Invalid email format: ${normalized.email}`);
    }

    console.log(`[Normalize] Customer ${normalized.email} | Orders: ${normalized.shopify_orders_count} | Spent: $${normalized.total_revenue}`);

    return normalized;
  }
});
10

Workflow Canvas > Deploy

Deploy the workflow and verify the live webhook

Click 'Deploy' in the top-right of the workflow canvas. Pipedream will activate the Shopify webhook and begin listening for real events. Place a real test order in your Shopify store (you can use a $0.01 product with a real payment method, then refund it). Within 30 seconds, check the workflow's 'Events' tab in Pipedream to confirm the run completed successfully. Then open HubSpot Contacts to verify the contact was created or updated with the correct data.

  1. 1Click the blue 'Deploy' button in the top-right corner
  2. 2Confirm the workflow status changes to 'Active' (green dot next to the workflow name)
  3. 3Place a test order in Shopify
  4. 4Open Pipedream > Workflows > [Your Workflow] > Events tab and confirm a successful run appears
  5. 5Open HubSpot > Contacts and verify the contact record shows the correct Shopify data
What you should see: The Events tab shows a completed run with all steps green. The HubSpot contact record shows the customer's name, email, phone, and a shopify_customer_id custom property populated.
Common mistake — Pipedream registers the Shopify webhook automatically on deploy, but if your Shopify store already has 50+ webhooks registered (Shopify's hard limit), the deploy will silently fail to register. Check Shopify Admin > Settings > Notifications > Webhooks and delete unused ones if you hit this ceiling.

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 n8n for this workflow

Use Pipedream for this if your team includes at least one person comfortable reading JavaScript. The Node.js code step is the real reason to pick Pipedream here — you can normalize Shopify's messy order payload, handle guest checkouts, lowercase emails, and validate fields all in one step before touching HubSpot's API. No other no-code platform gives you that control without forcing you into a separate code environment. The one scenario where you'd skip Pipedream: if your team is non-technical and you need this running in under an hour without touching code. In that case, Zapier's native Shopify-HubSpot integration gets you live faster, even if it's less flexible.

Cost

Pipedream's free tier gives you 10,000 credits per month. This workflow burns roughly 3-4 credits per order event (trigger + two code steps + two HubSpot API calls). At 500 orders/month, you're using about 2,000 credits — comfortably inside the free tier. At 2,500 orders/month, you're at 10,000 credits and hitting the free limit. The paid Basic plan at $19/month includes 100,000 credits, which covers 25,000 orders/month. Zapier's equivalent workflow costs $49/month at the Professional tier just to access multi-step Zaps with HubSpot. Pipedream is cheaper at every volume tier that involves code steps.

Tradeoffs

Zapier has one genuine advantage here: the native Shopify + HubSpot Zap template requires zero code and is ready in 15 minutes. If you've never written an async function, Zapier wins on setup time. Make's HTTP module lets you call HubSpot's batch contact API directly, which is more efficient at high order volumes — Pipedream's per-event model isn't designed for bulk backfill. n8n's self-hosted option costs nothing at any volume if you run it on a $6/month VPS, which undercuts Pipedream financially at scale. Power Automate integrates better if HubSpot is part of a Microsoft 365 org — the native connector handles OAuth refresh automatically in ways that Pipedream's connected accounts occasionally fumble. That said, Pipedream is still the right call for most DTC brands: the webhook speed, the Node.js flexibility, and the transparent per-run logging make debugging production issues 10x faster than hunting through Zapier's task history.

Three things you'll hit after go-live. First, Shopify's customer.total_spent field on order webhooks reflects the customer's lifetime spend at the moment the order is created — not after it's fulfilled or after refunds are applied. If you're using this field for HubSpot revenue segmentation, it will be wrong for customers who get refunds. Pull the value from the Shopify Customer API 5 minutes after the order fires instead. Second, HubSpot's contact search by email is case-sensitive in some API versions — '[email protected]' and '[email protected]' can return different results. Always lowercase before searching. Third, if your Shopify store uses multiple currencies, the total_spent value comes back in the store's default currency with no currency code attached. If you sell internationally and want to track USD-equivalent spend in HubSpot, you'll need to add a currency conversion step using the order's presentment_currency and exchange rate from Shopify's Markets API.

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 won't automatically flow back to Shopify. Build a second Pipedream workflow triggered by HubSpot's 'Contact Updated' webhook that patches the corresponding Shopify customer record using the stored shopify_customer_id property.
  • Create HubSpot Deals for Each Shopify OrderThis workflow syncs the customer, but the order itself doesn't exist in HubSpot. Extend the workflow by adding a HubSpot 'Create Deal' step after contact creation — map Shopify order total to deal amount, order ID to deal name, and associate it with the synced contact. This gives your sales team a full revenue view inside HubSpot.
  • Add Slack Alerts for High-Value CustomersAdd a conditional at the end of the workflow: if the customer's total_spent crosses $500, post a message to your #vip-customers Slack channel with their name, total spend, and a direct link to their HubSpot contact. Takes one extra Pipedream step and turns the sync into an active sales signal.

Related guides

Was this guide helpful?
HubSpot + Shopify overviewPipedream profile →