

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-timeUse case type
enrichmentReal-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.
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 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.
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Customer Email | email | |
| Recently Viewed Products | recently_viewed_products | |
| Product Interest Categories | product_interest_categories | |
| HubSpot Contact ID | hs_object_id | |
4 optional fields▸ show
| Last Product Viewed Date | last_product_viewed_date |
| Last Viewed Product ID | last_viewed_product_id |
| Last Viewed Product Title | last_viewed_product_title |
| Product View Count | product_view_count |
Step-by-Step Setup
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.
- 1Log in to HubSpot and click the gear icon (Settings) in the top navigation
- 2Select 'Properties' from the left sidebar under 'Data Management'
- 3Click the 'Create property' button in the top right
- 4Set Group to 'Contact Information', Label to 'Recently Viewed Products', Internal name to 'recently_viewed_products', Field type to 'Multi-line text'
- 5Repeat for 'Product Interest Categories' with internal name 'product_interest_categories'
- 6Click 'Create' after each property
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.
- 1In Shopify Admin, click 'Online Store' in the left sidebar
- 2Click 'Themes', then click 'Edit code' next to your active theme
- 3Open 'product.liquid' (or 'theme.liquid' if your theme uses sections)
- 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)
- 5Save the file — do not publish yet
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'.
- 1Go to make.powerautomate.com and sign in
- 2Click 'My flows' in the left sidebar
- 3Click '+ New flow' and choose 'Automated cloud flow'
- 4Name the flow 'Shopify Product View to HubSpot'
- 5In the trigger search box, type 'HTTP request' and select 'When an HTTP request is received'
- 6Click 'Create'
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.
- 1Click inside the 'When an HTTP request is received' trigger card
- 2Click 'Use sample payload to generate schema'
- 3Paste the sample JSON (see pro tip section for full schema)
- 4Click 'Done' — Power Automate generates the JSON Schema automatically
- 5Click 'Save' in the top right to generate the HTTP POST URL
- 6Click the trigger card again and copy the HTTP POST URL field value
- 7Paste this URL into the Shopify product.liquid script from Step 2 and re-save that file
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.
- 1Click '+ New step'
- 2In the action search bar, type 'Parse JSON'
- 3Select 'Parse JSON' under 'Data Operation'
- 4Click in the 'Content' field and select 'Body' from the dynamic content panel (from the HTTP trigger)
- 5Paste your JSON schema into the 'Schema' field
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.
- 1Click '+ New step'
- 2Search for 'HubSpot' in the connector search bar
- 3Select 'HubSpot' and choose 'Search for contacts'
- 4If prompted, click 'Sign in' and complete OAuth with your HubSpot account
- 5Set the filter property to 'Email', operator to 'equals', and value to the 'customer_email' dynamic content token
- 6Set the properties to retrieve to include 'recently_viewed_products' and 'product_interest_categories'
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.
- 1Click '+ New step'
- 2Select 'Control' and then 'Condition'
- 3In the left value field, select the 'Total' dynamic content token from the HubSpot Search for contacts step
- 4Set the operator to 'is greater than' and the right value to '0'
- 5In the 'If no' branch, click 'Add an action', search for 'Terminate', select it, and set Status to 'Succeeded'
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.
- 1Inside the 'Yes' branch, click 'Add an action'
- 2Search for 'Compose' and select the 'Data Operation: Compose' action
- 3Click in the 'Inputs' field and switch to the 'Expression' tab in the dynamic content panel
- 4Enter: concat(body('Search_for_contacts')?['results']?[0]?['properties']?['recently_viewed_products']?['value'], ' | ', body('Parse_JSON')?['product_title'])
- 5Click 'OK' and name this action 'Compose_Updated_Products'
- 6Repeat for a second Compose action named 'Compose_Updated_Categories' using product_type instead of product_title
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.
- 1Click 'Add an action' inside the 'Yes' branch
- 2Search for 'HubSpot' and select 'Update a contact'
- 3In 'Contact ID', click the Expression tab and enter: body('Search_for_contacts')?['results']?[0]?['id']
- 4Click '+ Add new property', select 'recently_viewed_products', and set the value to the Output of 'Compose_Updated_Products'
- 5Click '+ Add new property' again, select 'product_interest_categories', and set the value to the Output of 'Compose_Updated_Categories'
- 6Optionally add 'last_product_viewed_date' mapped to the expression: utcNow()
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.
- 1Click '+ New step' before the HubSpot search action in the Yes branch
- 2Select 'Control' then 'Scope'
- 3Move or rebuild the HubSpot Search, Condition, Compose, and Update actions inside the Scope
- 4Click '+ New step' after the Scope
- 5Click the three-dot menu on the new step and select 'Configure run after'
- 6Check 'has failed' and uncheck all other options
- 7Add a 'Send an email (V2)' or 'Post message in a chat or channel' action with the failure details
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.
- 1Open Postman and create a POST request to your HTTP trigger URL
- 2Set the body to raw JSON with customer_email, product_title, product_id, and product_type
- 3Click Send and check for a 202 response
- 4In Power Automate, click your flow name then 'Run history' to see the run appear
- 5Click the run to expand each step and confirm all show green
- 6Open HubSpot, find the test contact, and verify the property values updated correctly
- 7Toggle the flow to 'On' using the button at the top of the flow detail page
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
)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 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 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.
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 data — Now 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 companion — Build 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 metafields — Extend 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
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