How to Connect HubSpot to Shopify Plus Bidirectionally: A Technical Tutorial

Key Takeaways
- Native HubSpot-Shopify connector syncs contacts but not order deal stages bidirectionally
- Webhook-driven middleware achieves sub-60-second sync latency across both platforms
- Conflict resolution and deduplication consume 40% of integration project effort
- Full bidirectional integration costs USD $15K-40K depending on storefront count
- Always implement dead letter queues and daily reconciliation jobs for production sync
Quick Answer: Connect HubSpot to Shopify Plus bidirectionally by installing the native connector for basic contact sync, then building webhook-driven middleware that syncs orders to deals and pushes HubSpot lifecycle changes back to Shopify customer tags via the Admin API.
According to HubSpot's 2024 State of Marketing report, companies using bidirectional CRM-to-commerce integrations see 23% higher customer retention than those relying on one-way data pushes. Yet most Shopify Plus merchants I talk to across Asia-Pacific are still running one-directional syncs—contacts flow from Shopify to HubSpot, but lifecycle stage changes, deal updates, and segmentation data never flow back. That gap creates fractured customer journeys and lost revenue.
Related reading: Marketplace Seller Data Stack for Shopee Lazada: A Practical Build Guide
Related reading: LLM Token Efficiency Cost Benchmarking: APAC Workflow Data Across GPT-4o, Claude, Gemini
Related reading: AI Pushes B2B Ecommerce Platform Consolidation Across APAC
This tutorial walks you through how to connect HubSpot to Shopify Plus bidirectionally—not just the native connector, but real webhook-driven, API-level sync that handles contacts, orders, deal stages, and lifecycle properties in both directions. I'm writing this from direct experience: last year, Branch8 built this exact integration for a multi-brand retail group operating across Hong Kong and Singapore, syncing 380,000+ customer records with sub-60-second latency.
The native HubSpot-Shopify connector (available via HubSpot's connected apps marketplace) handles basic contact and product sync. It's a reasonable starting point. But if you need custom field mappings, order-to-deal pipeline sync, or real-time lifecycle updates flowing from HubSpot back into Shopify Plus metafields, you need to go further.
Prerequisites
Before you begin, confirm you have the following in place:
HubSpot Requirements
- HubSpot Professional or Enterprise plan (Marketing Hub or Sales Hub). The free tier lacks custom workflow actions and API rate limits are restrictive—1,000 calls per day versus 500,000 per day on Enterprise, per HubSpot's API documentation.
- A HubSpot private app with these scopes:
crm.objects.contacts.read,crm.objects.contacts.write,crm.objects.deals.read,crm.objects.deals.write,crm.schemas.contacts.read. - Custom properties already created for any Shopify-specific fields you want to sync (e.g.,
shopify_customer_id,shopify_total_spent,shopify_order_count).
Shopify Plus Requirements
- An active Shopify Plus store. Standard Shopify plans lack the checkout extensibility and higher API rate limits (currently 20 requests/second on Plus versus 2/second on basic, per Shopify's API rate limiting documentation) that make bidirectional sync viable at scale.
- A Shopify custom app with Admin API access:
read_customers,write_customers,read_orders,write_orders,read_products. - Shopify Flow enabled (included with Plus).
Infrastructure Requirements
- A middleware server or serverless function environment (AWS Lambda, Google Cloud Functions, or a VPS). We use AWS Lambda in ap-southeast-1 (Singapore) for APAC clients to minimize latency.
- Node.js 18+ or Python 3.11+ installed locally for development.
- A webhook relay service for local testing—we recommend ngrok or Cloudflare Tunnels.
Related reading: Developer Supply Chain Security Best Practices for APAC Teams
Here's the HubSpot private app creation step:
1# Navigate to HubSpot > Settings > Integrations > Private Apps > Create2# Set these scopes in the private app configuration:34Scopes required:5 crm.objects.contacts.read6 crm.objects.contacts.write7 crm.objects.deals.read8 crm.objects.deals.write9 crm.schemas.contacts.read10 crm.schemas.deals.read
And the Shopify custom app:
1# Navigate to Shopify Plus Admin > Settings > Apps and sales channels > Develop apps2# Create app with these Admin API scopes:34Admin API access scopes:5 read_customers6 write_customers7 read_orders8 write_orders9 read_products10 read_metaobjects11 write_metaobjects
Step 1: Install the Native HubSpot-Shopify Connector as Your Baseline
Start with the native integration. It handles basic contact sync and gives you a foundation to extend.
In your HubSpot account, navigate to the App Marketplace (the marketplace icon in the top navigation), search for "Shopify," and install the official integration. Authenticate with your Shopify Plus store URL.
Once connected, configure the default sync settings:
- Enable contact sync (bidirectional is supported natively for contacts since HubSpot's Operations Hub update in late 2023).
- Enable product sync (bidirectional).
- Map standard fields: email, first name, last name, phone.
Here's the critical limitation: the native connector syncs contacts and products bidirectionally, but orders only flow one way (Shopify → HubSpot). Deal stage changes in HubSpot don't propagate back. Customer tags and metafields don't sync at all. This is where the custom build begins.
Verify the native sync is working:
1# Test by creating a contact in HubSpot with a unique email2curl --request POST \3 --url https://api.hubapi.com/crm/v3/objects/contacts \4 --header 'Authorization: Bearer YOUR_HUBSPOT_TOKEN' \5 --header 'Content-Type: application/json' \6 --data '{7 "properties": {8 "email": "[email protected]",9 "firstname": "Sync",10 "lastname": "Test"11 }12 }'1314# Expected: This contact should appear in Shopify Customers within 10 minutes15# via the native connector. If it doesn't, check your sync rules in16# HubSpot > Data Management > Data Sync > Shopify.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Step 2: Set Up Shopify Webhooks for Real-Time Order and Customer Events
The native connector polls on intervals. For bidirectional sync with sub-minute latency, you need Shopify webhooks pushing events to your middleware.
Register webhooks via the Shopify Admin API:
1// register-shopify-webhooks.js2const Shopify = require('@shopify/shopify-api');34const SHOPIFY_STORE = 'your-store.myshopify.com';5const SHOPIFY_ACCESS_TOKEN = process.env.SHOPIFY_ACCESS_TOKEN;67const webhooksToRegister = [8 { topic: 'customers/create', address: 'https://your-middleware.com/webhooks/shopify/customer-create' },9 { topic: 'customers/update', address: 'https://your-middleware.com/webhooks/shopify/customer-update' },10 { topic: 'orders/create', address: 'https://your-middleware.com/webhooks/shopify/order-create' },11 { topic: 'orders/updated', address: 'https://your-middleware.com/webhooks/shopify/order-update' },12 { topic: 'orders/fulfilled', address: 'https://your-middleware.com/webhooks/shopify/order-fulfilled' },13 { topic: 'refunds/create', address: 'https://your-middleware.com/webhooks/shopify/refund-create' },14];1516async function registerWebhooks() {17 for (const webhook of webhooksToRegister) {18 const response = await fetch(19 `https://${SHOPIFY_STORE}/admin/api/2024-10/webhooks.json`,20 {21 method: 'POST',22 headers: {23 'Content-Type': 'application/json',24 'X-Shopify-Access-Token': SHOPIFY_ACCESS_TOKEN,25 },26 body: JSON.stringify({27 webhook: {28 topic: webhook.topic,29 address: webhook.address,30 format: 'json',31 },32 }),33 }34 );35 const data = await response.json();36 console.log(`Registered ${webhook.topic}:`, data.webhook?.id || data.errors);37 }38}3940registerWebhooks();
Run this script and verify all six webhooks are registered:
1node register-shopify-webhooks.js23# Expected output:4# Registered customers/create: 12345678905# Registered customers/update: 12345678916# Registered orders/create: 12345678927# Registered orders/updated: 12345678938# Registered orders/fulfilled: 12345678949# Registered refunds/create: 1234567895
Step 3: Set Up HubSpot Webhooks for Lifecycle and Deal Changes
This is the "back" in bidirectional. HubSpot needs to notify your middleware when lifecycle stages, deal stages, or contact properties change so those updates can flow to Shopify Plus.
HubSpot offers two approaches: workflow-based webhook actions and the Webhooks API (v3). We use workflow-based webhooks because they let marketing teams control which events trigger syncs without touching code.
Related reading: Copilot AI Code Insertion Security Risks: A Team Governance Playbook
In HubSpot, create a workflow:
- Trigger: Contact property change →
lifecyclestage - Action: Send a webhook (POST) to
https://your-middleware.com/webhooks/hubspot/lifecycle-change
Repeat for deal stage changes:
- Trigger: Deal property change →
dealstage - Action: Send a webhook (POST) to
https://your-middleware.com/webhooks/hubspot/deal-stage-change
The webhook payload from HubSpot workflows includes the object ID but not all properties. Your middleware needs to fetch the full record:
1// middleware/handlers/hubspot-lifecycle-change.js2async function handleLifecycleChange(req, res) {3 const { objectId } = req.body;45 // Fetch full contact from HubSpot6 const hsResponse = await fetch(7 `https://api.hubapi.com/crm/v3/objects/contacts/${objectId}?properties=email,lifecyclestage,shopify_customer_id,hs_lead_status`,8 {9 headers: {10 Authorization: `Bearer ${process.env.HUBSPOT_TOKEN}`,11 },12 }13 );14 const contact = await hsResponse.json();1516 const shopifyCustomerId = contact.properties.shopify_customer_id;17 if (!shopifyCustomerId) {18 console.log(`Contact ${objectId} has no Shopify ID, skipping reverse sync`);19 return res.status(200).json({ skipped: true });20 }2122 // Map HubSpot lifecycle stage to Shopify customer tag23 const tagMapping = {24 subscriber: 'lifecycle-subscriber',25 lead: 'lifecycle-lead',26 marketingqualifiedlead: 'lifecycle-mql',27 salesqualifiedlead: 'lifecycle-sql',28 opportunity: 'lifecycle-opportunity',29 customer: 'lifecycle-customer',30 evangelist: 'lifecycle-evangelist',31 };3233 const newTag = tagMapping[contact.properties.lifecyclestage];3435 // Update Shopify customer tags36 const shopifyResponse = await fetch(37 `https://${process.env.SHOPIFY_STORE}/admin/api/2024-10/customers/${shopifyCustomerId}.json`,38 {39 method: 'PUT',40 headers: {41 'Content-Type': 'application/json',42 'X-Shopify-Access-Token': process.env.SHOPIFY_ACCESS_TOKEN,43 },44 body: JSON.stringify({45 customer: {46 id: shopifyCustomerId,47 tags: newTag, // This replaces all tags; see Step 5 for tag merging48 },49 }),50 }51 );5253 const result = await shopifyResponse.json();54 console.log(`Updated Shopify customer ${shopifyCustomerId} with tag: ${newTag}`);55 return res.status(200).json({ success: true, shopifyCustomerId });56}
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Step 4: Build the Order-to-Deal Pipeline Sync
This is where most integrations fall short. Shopify orders need to map to HubSpot deals, and deal stage changes need to flow back as order tags or metafields in Shopify.
First, create a dedicated deal pipeline in HubSpot for e-commerce orders:
1curl --request POST \2 --url https://api.hubapi.com/crm/v3/pipelines/deals \3 --header 'Authorization: Bearer YOUR_HUBSPOT_TOKEN' \4 --header 'Content-Type: application/json' \5 --data '{6 "label": "Shopify Orders",7 "displayOrder": 1,8 "stages": [9 { "label": "Order Placed", "metadata": { "probability": "0.2" }, "displayOrder": 0 },10 { "label": "Payment Confirmed", "metadata": { "probability": "0.4" }, "displayOrder": 1 },11 { "label": "Fulfilled", "metadata": { "probability": "0.8" }, "displayOrder": 2 },12 { "label": "Delivered", "metadata": { "probability": "1.0" }, "displayOrder": 3 },13 { "label": "Refunded", "metadata": { "probability": "0.0" }, "displayOrder": 4 }14 ]15 }'1617# Save the pipeline ID and stage IDs from the response—you'll need them for mapping.
Now, in your middleware, handle the orders/create webhook from Shopify:
1// middleware/handlers/shopify-order-create.js2async function handleOrderCreate(req, res) {3 const order = req.body;45 // Find or create HubSpot contact by email6 const searchResponse = await fetch(7 'https://api.hubapi.com/crm/v3/objects/contacts/search',8 {9 method: 'POST',10 headers: {11 Authorization: `Bearer ${process.env.HUBSPOT_TOKEN}`,12 'Content-Type': 'application/json',13 },14 body: JSON.stringify({15 filterGroups: [{16 filters: [{ propertyName: 'email', operator: 'EQ', value: order.email }],17 }],18 }),19 }20 );21 const searchResult = await searchResponse.json();22 let contactId = searchResult.results?.[0]?.id;2324 if (!contactId) {25 // Create contact26 const createResponse = await fetch(27 'https://api.hubapi.com/crm/v3/objects/contacts',28 {29 method: 'POST',30 headers: {31 Authorization: `Bearer ${process.env.HUBSPOT_TOKEN}`,32 'Content-Type': 'application/json',33 },34 body: JSON.stringify({35 properties: {36 email: order.email,37 firstname: order.customer?.first_name,38 lastname: order.customer?.last_name,39 phone: order.customer?.phone,40 shopify_customer_id: String(order.customer?.id),41 },42 }),43 }44 );45 const newContact = await createResponse.json();46 contactId = newContact.id;47 }4849 // Create deal in the Shopify Orders pipeline50 const dealResponse = await fetch(51 'https://api.hubapi.com/crm/v3/objects/deals',52 {53 method: 'POST',54 headers: {55 Authorization: `Bearer ${process.env.HUBSPOT_TOKEN}`,56 'Content-Type': 'application/json',57 },58 body: JSON.stringify({59 properties: {60 dealname: `Shopify Order #${order.order_number}`,61 amount: order.total_price,62 pipeline: process.env.HUBSPOT_SHOPIFY_PIPELINE_ID,63 dealstage: process.env.HUBSPOT_STAGE_ORDER_PLACED,64 shopify_order_id: String(order.id),65 shopify_order_number: String(order.order_number),66 },67 }),68 }69 );70 const deal = await dealResponse.json();7172 // Associate deal with contact73 await fetch(74 `https://api.hubapi.com/crm/v4/objects/deals/${deal.id}/associations/contacts/${contactId}`,75 {76 method: 'PUT',77 headers: {78 Authorization: `Bearer ${process.env.HUBSPOT_TOKEN}`,79 'Content-Type': 'application/json',80 },81 body: JSON.stringify([{ associationCategory: 'HUBSPOT_DEFINED', associationTypeId: 3 }]),82 }83 );8485 console.log(`Created deal ${deal.id} for order #${order.order_number}`);86 return res.status(200).json({ dealId: deal.id });87}
Step 5: Handle the Hardest Part—Conflict Resolution and Deduplication
Bidirectional sync introduces a problem that unidirectional sync doesn't have: what happens when the same field is updated on both sides within the sync window?
When we built this for a Hong Kong-based multi-brand retailer with 12 Shopify Plus storefronts feeding into one HubSpot instance, conflict resolution was the single most time-consuming piece—roughly 40% of the 8-week project timeline. Here's how we approached it.
Last-Write-Wins with Timestamp Tracking
Store the last modification timestamp for each synced field in both systems. Shopify Plus metafields work well for this:
1// conflict-resolver.js2function resolveConflict(hubspotRecord, shopifyRecord, fieldName) {3 const hsUpdatedAt = new Date(hubspotRecord.properties[`${fieldName}_last_synced`] || 0);4 const shopifyUpdatedAt = new Date(shopifyRecord.updated_at);56 if (shopifyUpdatedAt > hsUpdatedAt) {7 return { winner: 'shopify', value: shopifyRecord[fieldName] };8 } else {9 return { winner: 'hubspot', value: hubspotRecord.properties[fieldName] };10 }11}1213// Tag merging (don't overwrite—merge)14function mergeShopifyTags(existingTags, newTag) {15 const tagArray = existingTags ? existingTags.split(', ') : [];16 // Remove any existing lifecycle tag17 const filtered = tagArray.filter(t => !t.startsWith('lifecycle-'));18 filtered.push(newTag);19 return filtered.join(', ');20}
Deduplication Strategy
Shopify identifies customers by email. HubSpot also uses email as the primary dedup key. But in APAC markets—particularly Hong Kong and Taiwan—customers frequently use multiple email addresses (personal Gmail for online orders, work email for B2B inquiries). According to Segment's 2023 Customer Data Report, the average consumer has 2.3 email identities across commerce platforms.
We recommend adding phone number as a secondary match key:
1async function findHubSpotContact(email, phone) {2 // Try email first3 let result = await searchHubSpotContacts('email', email);4 if (result) return result;56 // Fall back to phone7 if (phone) {8 result = await searchHubSpotContacts('phone', phone);9 if (result) return result;10 }1112 return null; // No match found, create new13}
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
How to Connect HubSpot with Shopify Using Flow Automation
Shopify Flow (included with Shopify Plus) adds another layer to the bidirectional connection. You can trigger Shopify-side automations based on data synced back from HubSpot.
For example, when your middleware writes a lifecycle-sql tag to a Shopify customer (based on a HubSpot lifecycle change), a Shopify Flow workflow can automatically:
- Apply a VIP discount code via Shopify Scripts
- Add the customer to a specific customer segment
- Trigger a Shopify Email or Klaviyo flow (relevant if you're evaluating HubSpot vs Klaviyo for transactional email)
Create this Flow in your Shopify Plus admin:
1Trigger: Customer tags updated2Condition: Customer tags contain "lifecycle-sql"3Action: Add customer to segment "Sales Qualified"4Action: Add tag "vip-pricing-eligible"
This closes the loop: HubSpot sales team qualifies a lead → lifecycle stage changes → middleware syncs the tag to Shopify → Shopify Flow applies the pricing tier. The customer sees VIP pricing on their next visit without any manual intervention.
Monitoring and Error Handling You Can't Skip
Webhook-driven integrations fail silently. Shopify retries failed webhooks 19 times over 48 hours (per Shopify's webhook documentation), but HubSpot workflow webhooks do not retry automatically.
At minimum, implement:
1// middleware/error-handler.js2const deadLetterQueue = []; // In production, use SQS, Redis, or a database34async function webhookHandler(req, res, processorFn) {5 try {6 // Verify webhook signature (Shopify HMAC or HubSpot signature)7 if (!verifySignature(req)) {8 return res.status(401).json({ error: 'Invalid signature' });9 }1011 // Idempotency check—prevent processing the same webhook twice12 const webhookId = req.headers['x-shopify-webhook-id'] || req.body?.webhookId;13 if (await isAlreadyProcessed(webhookId)) {14 return res.status(200).json({ skipped: 'duplicate' });15 }1617 await processorFn(req.body);18 await markAsProcessed(webhookId);1920 return res.status(200).json({ success: true });21 } catch (error) {22 console.error('Webhook processing failed:', error);2324 // Dead letter queue for manual retry25 deadLetterQueue.push({26 timestamp: new Date().toISOString(),27 payload: req.body,28 error: error.message,29 source: req.path,30 });3132 // Return 200 to prevent Shopify from retrying (you'll retry from DLQ)33 return res.status(200).json({ queued: true });34 }35}
We also set up a daily reconciliation job that compares record counts and checksums between HubSpot and Shopify. In our Hong Kong retail project, this caught 127 desynchronized records in the first week that would have gone unnoticed without it.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
HubSpot Pricing Considerations for Shopify Plus Integration
A question we get from every APAC enterprise client: what does this actually cost?
The Shopify HubSpot integration itself is free in HubSpot's App Marketplace. But the features you need for proper bidirectional sync are not:
- HubSpot Marketing Hub Professional: starts at USD $800/month (per HubSpot's 2024 pricing page), required for workflow-based webhooks
- HubSpot Operations Hub Professional: USD $800/month, required for custom coded workflow actions and data quality automation
- Shopify Plus: starts at USD $2,300/month (per Shopify's Plus pricing)
- Middleware hosting: USD $50-200/month on AWS Lambda for moderate volume (under 100,000 events/day)
Custom development cost for a full bidirectional integration runs USD $15,000-40,000 depending on complexity, number of storefronts, and the field mapping requirements. The Hong Kong project I referenced earlier came in at USD $28,000 for 12 storefronts with shared customer data, which was about 60% less than the client's previous estimate for a comparable Salesforce Commerce Cloud integration.
What to Do Next
You now have the architecture and code to connect HubSpot to Shopify Plus bidirectionally. Before you deploy to production, run through this decision checklist:
Pre-Launch Checklist
- Field mapping document: Have you mapped every field that needs to sync in both directions? Export this as a spreadsheet shared between your marketing and engineering teams.
- Conflict resolution rules: For each bidirectional field, have you defined which system wins on conflict? Document this per field, not as a blanket rule.
- Rate limit buffer: HubSpot allows 500,000 API calls/day on Enterprise; Shopify Plus allows 20 requests/second. Have you calculated your peak event volume against these limits?
- Webhook signature verification: Are you validating HMAC signatures on every incoming webhook? Never skip this in production.
- Dead letter queue: Do you have a mechanism to capture and retry failed sync events?
- Reconciliation job: Is there a scheduled job comparing record counts and key field values across systems?
- Rollback plan: Can you disable the bidirectional sync without losing data if something goes wrong?
- APAC data residency: If you're operating in Singapore, Australia, or other markets with data residency requirements, confirm your middleware region and HubSpot data center location comply with local regulations. Australia's Privacy Act 1988 and Singapore's PDPA both have cross-border transfer provisions.
If you're running Shopify Plus across multiple APAC markets and need help scoping or building a bidirectional HubSpot integration, reach out to Branch8. We've done this across Hong Kong, Singapore, and Australia for retail and D2C brands processing over 1 million orders annually.
Ready to Transform Your Ecommerce Operations?
Branch8 specializes in ecommerce platform implementation and AI-powered automation solutions. Contact us today to discuss your ecommerce automation strategy.
Sources
- HubSpot API Rate Limits: https://developers.hubspot.com/docs/api/usage-details
- Shopify Webhooks Documentation: https://shopify.dev/docs/api/admin-rest/2024-10/resources/webhook
- Shopify Plus API Rate Limits: https://shopify.dev/docs/api/usage/rate-limits
- HubSpot 2024 State of Marketing Report: https://www.hubspot.com/state-of-marketing
- Segment Customer Data Report 2023: https://segment.com/good-data-better-decisions/
- HubSpot Pricing: https://www.hubspot.com/pricing
- Shopify Plus Pricing: https://www.shopify.com/plus
- Australia Privacy Act 1988 (APP 8 – Cross-border disclosure): https://www.oaic.gov.au/privacy/australian-privacy-principles
FAQ
The fastest way is through HubSpot's native Shopify integration in the App Marketplace, which handles basic contact and product sync. Navigate to Settings > Integrations > Connected Apps, search for Shopify, and authenticate with your store URL. For bidirectional sync of orders, deal stages, and custom fields, you'll need a middleware layer using webhooks and API calls as described in this tutorial.
About the Author
Matt Li
Co-Founder & CEO, Branch8 & Second Talent
Matt Li is Co-Founder and CEO of Branch8, a Y Combinator-backed (S15) Adobe Solution Partner and e-commerce consultancy headquartered in Hong Kong, and Co-Founder of Second Talent, a global tech hiring platform ranked #1 in Global Hiring on G2. With 12 years of experience in e-commerce strategy, platform implementation, and digital operations, he has led delivery of Adobe Commerce Cloud projects for enterprise clients including Chow Sang Sang, HomePlus (HKBN), Maxim's, Hong Kong International Airport, Hotai/Toyota, and Evisu. Prior to founding Branch8, Matt served as Vice President of Mid-Market Enterprises at HSBC. He serves as Vice Chairman of the Hong Kong E-Commerce Business Association (HKEBA). A self-taught software engineer, Matt graduated from the University of Toronto with a Bachelor of Commerce in Finance and Economics.