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

How to Track Shopify Product Views in HubSpot with Power Automate

Captures Shopify product page views and writes the browsed product names and categories to a HubSpot contact property — automatically, per customer.

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 already on Microsoft 365 who want to enrich HubSpot contact records with Shopify browsing data without writing backend code.

Not ideal for

Stores with anonymous-only traffic — this flow only works when the browsing customer is identified and matched to a HubSpot contact by email.

Sync type

real-time

Use case type

enrichment

Real-World Example

💡

A 12-person DTC brand sells outdoor gear through Shopify and tracks leads in HubSpot. Their marketing team was sending the same email campaign to every contact regardless of what products they had browsed. After setting up this flow, HubSpot contact records automatically show which product categories each customer viewed — sleeping bags, tents, hydration gear — and the team segments campaign lists based on those properties. Open rates on targeted sends went from 18% to 31% in the first month.

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 Power Automate

Copy the pre-built Power Automate blueprint and paste it straight into Power Automate. 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.

HubSpot account with contact write permissions and API access (Professional tier or above for custom properties via API)
Shopify store with theme editing access and the ability to modify product.liquid or theme.liquid
Microsoft 365 account with Power Automate access (the HTTP trigger is available on the standard per-user plan — it is not on the free tier)
HubSpot custom contact properties created for 'recently_viewed_products' and 'product_interest_categories' before building the flow
Shopify store must have customer accounts enabled so that logged-in customer emails are available in the storefront JavaScript context

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Customer Emailemail
Recently Viewed Productsrecently_viewed_products
Product Interest Categoriesproduct_interest_categories
HubSpot Contact IDhs_object_id
4 optional fields▸ show
Last Product Viewed Datelast_product_viewed_date
Last Viewed Product IDlast_viewed_product_id
Last Viewed Product Titlelast_viewed_product_title
Product View Countproduct_view_count

Step-by-Step Setup

1

HubSpot > Settings > Properties > Contact Properties > Create property

Create custom contact properties in HubSpot

Before building the flow, you need somewhere in HubSpot to store browsing data. In HubSpot, go to Settings > Properties, select Contact properties, and click Create property. Create two properties: one called 'Recently Viewed Products' (internal name: recently_viewed_products, type: Multi-line text) and one called 'Product Interest Categories' (internal name: product_interest_categories, type: Multi-line text). These will hold the pipe-delimited list of product titles and category names that Power Automate writes in later steps.

  1. 1Log in to HubSpot and click the gear icon (Settings) in the top navigation
  2. 2Select 'Properties' from the left sidebar under 'Data Management'
  3. 3Click the 'Create property' button in the top right
  4. 4Set Group to 'Contact Information', Label to 'Recently Viewed Products', Internal name to 'recently_viewed_products', Field type to 'Multi-line text'
  5. 5Repeat for 'Product Interest Categories' with internal name 'product_interest_categories'
  6. 6Click 'Create' after each property
What you should see: Both properties appear in your HubSpot Contact Properties list and are available as target fields when updating contacts via API.
Common mistake — HubSpot internal property names cannot be changed after creation — double-check spelling before saving. The internal names used here ('recently_viewed_products', 'product_interest_categories') must match exactly what you reference in Power Automate later.
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
2

Shopify Admin > Online Store > Themes > Edit code > product.liquid

Set up a Shopify webhook for product views

Shopify does not have a native 'product viewed' webhook event in its standard webhook catalog — product views must be captured via a Storefront script or a custom Shopify pixel that fires an HTTP request. In your Shopify admin, go to Online Store > Themes > Edit code, and locate the product.liquid or theme.liquid file. You will add a small JavaScript snippet that fires a POST request to your Power Automate HTTP trigger URL (which you will get in Step 4) whenever a logged-in customer loads a product page. The payload should include the customer email, product title, product ID, and product type.

  1. 1In Shopify Admin, click 'Online Store' in the left sidebar
  2. 2Click 'Themes', then click 'Edit code' next to your active theme
  3. 3Open 'product.liquid' (or 'theme.liquid' if your theme uses sections)
  4. 4Paste the JavaScript payload snippet at the bottom of the file, inside a <script> tag (you will insert the Power Automate URL after Step 4)
  5. 5Save the file — do not publish yet
What you should see: The product.liquid file is saved with the script block visible at the bottom. No errors appear in the Shopify code editor.
Common mistake — This script only fires for logged-in Shopify customers. If your store uses guest checkout as the primary flow, most product views will not include a customer email and the HubSpot lookup in Step 6 will fail. Confirm your store prompts login before browsing, or use Shopify's Customer Accounts feature.
3

make.powerautomate.com > My flows > + New flow > Automated cloud flow

Open Power Automate and create a new Automated cloud flow

Go to make.powerautomate.com and sign in with your Microsoft account. From the left sidebar, click 'My flows', then click '+ New flow' and select 'Automated cloud flow'. Name the flow something like 'Shopify Product View → HubSpot Contact Update'. On the trigger picker screen, search for 'When an HTTP request is received' — this is the built-in Power Automate trigger that gives you a public URL Shopify can POST to. Select it and click 'Create'.

  1. 1Go to make.powerautomate.com and sign in
  2. 2Click 'My flows' in the left sidebar
  3. 3Click '+ New flow' and choose 'Automated cloud flow'
  4. 4Name the flow 'Shopify Product View to HubSpot'
  5. 5In the trigger search box, type 'HTTP request' and select 'When an HTTP request is received'
  6. 6Click 'Create'
What you should see: The flow canvas opens with the 'When an HTTP request is received' trigger card expanded and a blank 'HTTP POST URL' field visible — the URL generates after you save the flow for the first time.
4

Flow canvas > When an HTTP request is received > Use sample payload to generate schema

Configure the HTTP trigger and define the JSON schema

In the trigger card, click 'Use sample payload to generate schema'. Paste in a sample JSON body that matches what your Shopify script will send. This tells Power Automate exactly which fields to expect and makes them available as dynamic content in later steps. Save the flow once to generate the HTTP POST URL, then copy that URL — you will paste it into the Shopify product.liquid script from Step 2.

  1. 1Click inside the 'When an HTTP request is received' trigger card
  2. 2Click 'Use sample payload to generate schema'
  3. 3Paste the sample JSON (see pro tip section for full schema)
  4. 4Click 'Done' — Power Automate generates the JSON Schema automatically
  5. 5Click 'Save' in the top right to generate the HTTP POST URL
  6. 6Click the trigger card again and copy the HTTP POST URL field value
  7. 7Paste this URL into the Shopify product.liquid script from Step 2 and re-save that file
What you should see: The trigger card now shows a filled HTTP POST URL (a long make.powerautomate.com URL). The JSON Schema field is populated with properties for customer_email, product_title, product_id, and product_type.
Common mistake — The HTTP POST URL changes if you delete and recreate the trigger. If you ever rebuild this step, update the URL in your Shopify theme script immediately or product view events will start failing silently.
Power Automate
+
click +
search apps
HubSpot
HU
HubSpot
Configure the HTTP trigger a…
HubSpot
HU
module added
5

Flow canvas > + New step > Data Operation > Parse JSON

Add a Parse JSON action to extract payload fields

Click '+ New step' below the trigger. Search for 'Parse JSON' and select the 'Data Operation: Parse JSON' action. In the Content field, insert the Body dynamic content token from the HTTP trigger. In the Schema field, paste the same schema you generated in Step 4. This step makes individual fields like customer_email and product_title accessible as named tokens throughout the rest of the flow.

  1. 1Click '+ New step'
  2. 2In the action search bar, type 'Parse JSON'
  3. 3Select 'Parse JSON' under 'Data Operation'
  4. 4Click in the 'Content' field and select 'Body' from the dynamic content panel (from the HTTP trigger)
  5. 5Paste your JSON schema into the 'Schema' field
What you should see: The Parse JSON card shows 'Body' in the Content field and a valid schema below it. When you click into a later action's field, the dynamic content panel shows tokens for customer_email, product_title, product_id, and product_type.
Common mistake — If your Shopify script sends any field as a number (e.g., product_id as an integer), make sure the schema type matches — declaring it as 'string' when the payload sends an integer causes Parse JSON to fail at runtime with a 'schema validation' error.
6

Flow canvas > + New step > HubSpot > Search for contacts

Look up the HubSpot contact by email

Click '+ New step'. Search for 'HubSpot' and select the HubSpot connector. Choose the action 'Search for contacts'. If this is your first HubSpot action, Power Automate will prompt you to create a Connection — click 'Sign in' and authenticate with your HubSpot account. In the filter field, set the search to match contacts where the 'Email' property equals the customer_email token from the Parse JSON step. This returns the HubSpot contact record including their current property values.

  1. 1Click '+ New step'
  2. 2Search for 'HubSpot' in the connector search bar
  3. 3Select 'HubSpot' and choose 'Search for contacts'
  4. 4If prompted, click 'Sign in' and complete OAuth with your HubSpot account
  5. 5Set the filter property to 'Email', operator to 'equals', and value to the 'customer_email' dynamic content token
  6. 6Set the properties to retrieve to include 'recently_viewed_products' and 'product_interest_categories'
What you should see: The action card shows your HubSpot connection with a green checkmark. When you run a test, the output shows a contact object with the matching email and existing property values.
Common mistake — The HubSpot Power Automate connector uses OAuth with your personal HubSpot account. If that account is deactivated or loses portal access, the entire flow breaks. Use a dedicated service account or integration user in HubSpot instead of a personal login.
7

Flow canvas > + New step > Control > Condition

Add a Condition to handle contact-not-found cases

The search in Step 6 returns zero results if the browsing customer has no HubSpot contact record. You need to handle this before trying to update a contact that does not exist. Click '+ New step' and add a 'Condition' control action. Set the condition to check if the 'Total' output from the HubSpot search action is greater than 0. In the 'If yes' branch, continue with the update steps. In the 'If no' branch, add a 'Terminate' action set to 'Succeeded' — this cleanly exits the flow without logging an error for unmatched visitors.

  1. 1Click '+ New step'
  2. 2Select 'Control' and then 'Condition'
  3. 3In the left value field, select the 'Total' dynamic content token from the HubSpot Search for contacts step
  4. 4Set the operator to 'is greater than' and the right value to '0'
  5. 5In the 'If no' branch, click 'Add an action', search for 'Terminate', select it, and set Status to 'Succeeded'
What you should see: The canvas shows a Condition card with a 'Yes' branch leading to the next steps and a 'No' branch containing the Terminate action.
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

Flow canvas > If yes branch > + Add an action > Data Operation > Compose

Build the updated product interest string

Inside the 'If yes' branch, add a 'Compose' action to construct the updated 'Recently Viewed Products' value. You want to append the new product_title to whatever is already stored in the contact's recently_viewed_products property — without overwriting previous views. In the Inputs field of the Compose action, write an expression that concatenates the existing property value with a pipe separator and the new product title. Use the expression: concat(body('Search_for_contacts')?['results']?[0]?['properties']?['recently_viewed_products']?['value'], ' | ', triggerBody()?['product_title']). Do the same in a second Compose action for product_interest_categories using product_type as the appended value.

  1. 1Inside the 'Yes' branch, click 'Add an action'
  2. 2Search for 'Compose' and select the 'Data Operation: Compose' action
  3. 3Click in the 'Inputs' field and switch to the 'Expression' tab in the dynamic content panel
  4. 4Enter: concat(body('Search_for_contacts')?['results']?[0]?['properties']?['recently_viewed_products']?['value'], ' | ', body('Parse_JSON')?['product_title'])
  5. 5Click 'OK' and name this action 'Compose_Updated_Products'
  6. 6Repeat for a second Compose action named 'Compose_Updated_Categories' using product_type instead of product_title
What you should see: Both Compose actions show the expression text. When you test with a sample, the output of each Compose shows the old value plus ' | ' plus the new product or category name.
Common mistake — If the contact's recently_viewed_products property is null (first ever visit), the concat expression produces 'null | Product Name' — the string literal 'null'. Wrap the expression in a coalesce: concat(coalesce(body('Search_for_contacts')?['results']?[0]?['properties']?['recently_viewed_products']?['value'], ''), ' | ', body('Parse_JSON')?['product_title']) to handle the empty case cleanly.
9

Flow canvas > If yes branch > + Add an action > HubSpot > Update a contact

Update the HubSpot contact properties

Still inside the 'Yes' branch, add another HubSpot action. Search for 'Update a contact' and select it. In the Contact ID field, use the ID from the first result of the search: body('Search_for_contacts')?['results']?[0]?['id']. Map the 'recently_viewed_products' property to the Output of the Compose_Updated_Products action. Map 'product_interest_categories' to the Output of the Compose_Updated_Categories action. You can also write additional static or dynamic fields here such as last_product_viewed_date using the utcNow() expression.

  1. 1Click 'Add an action' inside the 'Yes' branch
  2. 2Search for 'HubSpot' and select 'Update a contact'
  3. 3In 'Contact ID', click the Expression tab and enter: body('Search_for_contacts')?['results']?[0]?['id']
  4. 4Click '+ Add new property', select 'recently_viewed_products', and set the value to the Output of 'Compose_Updated_Products'
  5. 5Click '+ Add new property' again, select 'product_interest_categories', and set the value to the Output of 'Compose_Updated_Categories'
  6. 6Optionally add 'last_product_viewed_date' mapped to the expression: utcNow()
What you should see: The Update a contact card shows three mapped properties: recently_viewed_products, product_interest_categories, and optionally last_product_viewed_date. Test run returns a 200 response from HubSpot.
10

Flow canvas > + New step > Control > Scope

Add error handling with a Scope and Send an HTTP request fallback

Wrap Steps 6 through 9 in a 'Scope' control action so you can catch failures. Click the three-dot menu on each action inside the Yes branch and use 'Move inside Scope' (or add a Scope first and rebuild actions inside it). After the Scope, add a 'Run after' configured action — a second action set to run only if the Scope fails. Inside that fallback, add a 'Send an email (V2)' or a Teams notification to alert your team that a contact update failed, including the customer_email value so the failure is actionable.

  1. 1Click '+ New step' before the HubSpot search action in the Yes branch
  2. 2Select 'Control' then 'Scope'
  3. 3Move or rebuild the HubSpot Search, Condition, Compose, and Update actions inside the Scope
  4. 4Click '+ New step' after the Scope
  5. 5Click the three-dot menu on the new step and select 'Configure run after'
  6. 6Check 'has failed' and uncheck all other options
  7. 7Add a 'Send an email (V2)' or 'Post message in a chat or channel' action with the failure details
What you should see: The canvas shows a Scope block containing your main logic, and a fallback notification action below it with a run-after badge showing 'has failed'.
Common mistake — Power Automate's 'Configure run after' setting is easy to misconfigure — if you accidentally leave 'is successful' checked alongside 'has failed', your error notification fires on every successful run too. Uncheck everything except 'has failed'.
11

make.powerautomate.com > My flows > [Flow name] > Run history

Test end-to-end and turn on the flow

Use a tool like Postman or curl to POST a test payload to your HTTP trigger URL with a real customer email that exists in HubSpot. Check the flow run history at make.powerautomate.com > My flows > [your flow] > Run history. Each step should show a green checkmark. Then open the matching HubSpot contact and confirm the recently_viewed_products and product_interest_categories fields show the test product data. Once confirmed, enable the Shopify theme script by updating any remaining placeholder values and verifying it fires on your staging store before going live.

  1. 1Open Postman and create a POST request to your HTTP trigger URL
  2. 2Set the body to raw JSON with customer_email, product_title, product_id, and product_type
  3. 3Click Send and check for a 202 response
  4. 4In Power Automate, click your flow name then 'Run history' to see the run appear
  5. 5Click the run to expand each step and confirm all show green
  6. 6Open HubSpot, find the test contact, and verify the property values updated correctly
  7. 7Toggle the flow to 'On' using the button at the top of the flow detail page
What you should see: Run history shows a succeeded run with all steps green. The HubSpot contact record displays the new product interest data in the custom properties you created in Step 1.

This Power Automate expression block goes into the 'Inputs' field of the Compose action in Step 8. It handles the null-safe concatenation for both product titles and categories, deduplicates repeated category entries, and caps the stored product list at the 10 most recent views to keep the HubSpot property value from growing unbounded. Paste each expression into its own Compose action using the Expression tab in the dynamic content panel.

JavaScript — Code Step// Compose action 1: Compose_Updated_Products
▸ Show code
// Compose action 1: Compose_Updated_Products
// Safely appends new product title; caps list at 10 items
// Paste into the Inputs field > Expression tab

... expand to see full code

// Compose action 1: Compose_Updated_Products
// Safely appends new product title; caps list at 10 items
// Paste into the Inputs field > Expression tab
if(
  equals(coalesce(body('Search_for_contacts')?['results']?[0]?['properties']?['recently_viewed_products']?['value'], ''), ''),
  body('Parse_JSON')?['product_title'],
  join(
    take(
      split(
        concat(body('Search_for_contacts')?['results']?[0]?['properties']?['recently_viewed_products']?['value'], ' | ', body('Parse_JSON')?['product_title']),
        ' | '
      ),
      10
    ),
    ' | '
  )
)

// Compose action 2: Compose_Updated_Categories
// Appends new category and deduplicates using union()
// Paste into second Compose action Inputs field > Expression tab
join(
  union(
    split(
      coalesce(body('Search_for_contacts')?['results']?[0]?['properties']?['product_interest_categories']?['value'], ''),
      ' | '
    ),
    array(body('Parse_JSON')?['product_type'])
  ),
  ' | '
)

// Compose action 3: Compose_View_Count
// Increments product view count safely from null baseline
add(
  int(coalesce(body('Search_for_contacts')?['results']?[0]?['properties']?['product_view_count']?['value'], '0')),
  1
)
Power Automate
▶ Test flow
executed
HubSpot
Shopify
Shopify
🔔 notification
received

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 Power Automate for this if your organization is already on Microsoft 365 and your team does not want to manage another SaaS subscription. The HTTP trigger is solid, the HubSpot connector covers what you need, and if your store does fewer than 5,000 product views per day, you will stay well within the 40,000 daily action limit on a standard per-user plan. The second reason to pick Power Automate here is IT governance — many enterprise teams require that integrations live within the Microsoft ecosystem for compliance reasons. If neither of those apply to you and you just want the fastest setup, Make gets this workflow running in about 40 minutes with less expression syntax gymnastics.

Cost

Cost math: the Power Automate per-user plan runs $15/month. Each flow run for this workflow consumes roughly 6-8 actions (trigger, parse JSON, HubSpot search, condition, two compose actions, one update). At 3,000 product views per day from logged-in customers, that is 18,000-24,000 actions per day — comfortably under the 40,000 limit, so no overage. If you are on the Power Automate per-flow plan at $100/month per flow, this single workflow costs $100/month flat. Make handles the same volume for around $9/month on the Core plan, which includes 10,000 operations — at 6 operations per scenario run and 3,000 daily views, you would need the $16/month plan. Zapier at this volume would run $49-$73/month depending on tasks. Power Automate wins on cost only if you already pay for Microsoft 365 E3 or higher, which includes Power Automate per-user at no additional charge.

Tradeoffs

Make handles the field transformation logic for this use case more cleanly — the built-in text functions like split(), join(), and deduplicate() mean you do not need to write expression formulas by hand. Zapier has a Shopify 'New Customer Event' trigger that can catch product views natively in some configurations, removing the need for a custom theme script entirely — that is a genuine leg up for non-technical teams. n8n wins for teams who want full control: you can write the deduplication and cap logic in a Function node with ten lines of JavaScript and skip the three Compose action workaround entirely. Pipedream is the fastest option if you are comfortable with Node.js — their Shopify source handles the webhook registration automatically, so you skip the manual theme script step. Power Automate is still the right call here when Microsoft 365 licensing is already in play or when IT requires all integrations to stay within Azure and Microsoft infrastructure.

Three things you will hit after setup. First: the recently_viewed_products property grows without bound unless you cap it — a contact who browses daily for a year ends up with a 3,000-character property value that breaks HubSpot's 65,536-character limit eventually and makes segmentation lists unusable. Cap the list at 10-15 items using the take() expression from the pro tip. Second: HubSpot's Search contacts API endpoint returns archived contacts alongside active ones if you do not filter — you can silently update a deleted contact record and think the flow worked when no active contact was touched. Add the archived=false filter on the search action. Third: Power Automate's HTTP trigger does not validate the source of incoming requests by default — anyone who discovers your trigger URL can POST fake product view events to it. Add a shared secret header check in the flow using a Condition at the top that validates a custom header value, and configure your Shopify script to send that same secret header on every request.

Ideas for what to build next

  • Trigger HubSpot email workflows from product interest dataNow that HubSpot contacts have product category data, create HubSpot Workflows that enroll contacts in targeted email sequences when product_interest_categories contains 'Tents' or 'Hydration'. This turns passive browsing data into active campaign triggers without any additional automation.
  • Add a Shopify abandoned-browse flow as a companionBuild a second Power Automate scheduled flow that queries HubSpot daily for contacts whose last_product_viewed_date was 24-48 hours ago and who have not placed a Shopify order since — then send them a browse-abandonment email via HubSpot. This closes the loop between interest tracking and conversion.
  • Write product interest back to Shopify customer metafieldsExtend the flow to also write product_interest_categories to a Shopify customer metafield using the Shopify Admin API. This makes the interest data available to your Shopify storefront theme for real-time personalized product recommendations on the homepage without needing HubSpot as an intermediary.

Related guides

Was this guide helpful?
HubSpot + Shopify overviewPower Automate profile →