

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-timeUse case type
syncReal-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.
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
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.
Optional
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Email Address | email | |
| First Name | firstname | |
| Last Name | lastname | |
| Shopify Customer ID | shopify_customer_id | |
6 optional fields▸ show
| Phone Number | phone |
| City | city |
| Country Code | country |
| Total Spent | total_revenue |
| Orders Count | shopify_orders_count |
| Last Order Date | shopify_last_order_date |
Step-by-Step Setup
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.
- 1Click 'New Workflow' in the top-right of the Pipedream dashboard
- 2Click the pencil icon next to 'My Workflow' and rename it to 'Shopify → HubSpot Customer Sync'
- 3Click 'Save' to confirm the name
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.
- 1Click the grey 'Add a trigger' block on the canvas
- 2Type 'Shopify' in the search field and click the Shopify app icon
- 3Select 'New Order (Instant)' from the trigger list
- 4Click 'Connect Shopify Account' and enter your store subdomain (e.g. mystore.myshopify.com)
- 5Click 'Save and continue'
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.
- 1Click 'Generate Test Event' in the trigger configuration panel
- 2Wait for Pipedream to fetch the most recent Shopify order
- 3Expand the 'customer' object in the event payload on the right panel
- 4Confirm you can see fields: email, first_name, last_name, phone, total_spent
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.
- 1Click the '+' icon below the trigger block
- 2Select 'Run Node.js code' from the step options
- 3Clear the default code in the editor
- 4Paste the transformation code from the Pro Tip section
- 5Click 'Test' to run the step against your test event
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;
}
});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.
- 1Click the '+' icon below the Node.js code step
- 2Search for 'HubSpot' and click the HubSpot app icon
- 3Select 'Find Contact' from the action list
- 4Connect your HubSpot account via 'Connect HubSpot Account'
- 5Set Property Name to 'email' and click the '{' icon to map Property Value to steps.nodejs.$return_value.email
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.
- 1Click '+' below the Find Contact step
- 2Select 'Run Node.js code'
- 3Write a conditional that checks if steps.find_contact.$return_value?.id exists
- 4Export contactId and isExisting as return values for the next step
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.
- 1Click '+' and select HubSpot > Create Contact
- 2Click '{' next to Email and select steps.nodejs.$return_value.email
- 3Map firstname, lastname, phone, city, and country_code from the same source
- 4Add a custom property 'shopify_customer_id' and map it to steps.trigger.event.customer.id
- 5Click 'Test' to verify the contact is created in HubSpot sandbox
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.
- 1Click '+' and select HubSpot > Update Contact
- 2Set Contact ID to steps.conditional.$return_value.contactId
- 3Map firstname, lastname, phone, city, and country_code from steps.nodejs.$return_value
- 4Add the field total_spent and map it to steps.trigger.event.customer.total_spent
- 5Click 'Test' using a test event with an existing customer email
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.
- 1Click '+' and select 'Run Node.js code'
- 2Write a console.log statement outputting the Shopify order ID, HubSpot contact ID, and action taken (created vs updated)
- 3Return a summary object with the same fields
- 4Click 'Test' and open the 'Logs' tab to confirm the output appears
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;
}
});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.
- 1Click the blue 'Deploy' button in the top-right corner
- 2Confirm the workflow status changes to 'Active' (green dot next to the workflow name)
- 3Place a test order in Shopify
- 4Open Pipedream > Workflows > [Your Workflow] > Events tab and confirm a successful run appears
- 5Open HubSpot > Contacts and verify the contact record shows the correct Shopify data
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
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.
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.
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 Shopify — When 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 Order — This 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 Customers — Add 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
How to Share Notion Meeting Notes to Slack with Pipedream
~15 min setup
How to Share Notion Meeting Notes to Slack with Power Automate
~15 min setup
How to Share Notion Meeting Notes to Slack with n8n
~20 min setup
How to Send Notion Meeting Notes to Slack with Zapier
~8 min setup
How to Share Notion Meeting Notes to Slack with Make
~12 min setup
How to Create Notion Tasks from Slack with Pipedream
~15 min setup