

How to Recover Abandoned Carts with Pipedream, Shopify & HubSpot
Fires a HubSpot email sequence automatically when a Shopify customer abandons their cart, using Pipedream webhooks and Node.js to enroll or update the contact in real time.
Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.

Best for
Dev-comfortable e-commerce teams who need custom cart-abandonment logic — like skipping sequences for wholesale customers or adjusting delay windows by cart value.
Not ideal for
Shopify merchants who want abandoned cart emails without writing any code — use Klaviyo's native Shopify integration or Zapier instead.
Sync type
real-timeUse case type
notificationReal-World Example
A 12-person DTC apparel brand uses this to enroll customers in a three-email HubSpot sequence the moment Shopify fires a cart abandonment webhook. Before this, the marketing team manually exported abandoned cart CSVs every morning and bulk-enrolled contacts — a 45-minute daily task that meant some customers got emails 18+ hours after abandoning. Now the first email goes out within 4 minutes of abandonment.
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.
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Customer Email | email | |
| First Name | firstname | |
| Abandoned Cart Value | abandoned_cart_value | |
| Abandoned Checkout URL | abandoned_checkout_url | |
5 optional fields▸ show
| Last Name | lastname |
| Abandoned Cart Item Count | abandoned_cart_item_count |
| Cart Currency | abandoned_cart_currency |
| Checkout Created At | abandoned_cart_date |
| Phone Number | phone |
Step-by-Step Setup
pipedream.com > Workflows > New Workflow
Create a new Pipedream workflow
Go to pipedream.com and click 'New Workflow' in the top-right corner of the Workflows dashboard. Give the workflow a name like 'Shopify → HubSpot Abandoned Cart'. You'll land on a blank canvas with a trigger slot at the top and an empty steps area below. Pipedream generates a unique HTTPS endpoint URL the moment you add an HTTP trigger — you'll use this URL in Shopify next.
- 1Click 'New Workflow' in the top-right of the Workflows dashboard
- 2Enter a workflow name: 'Shopify → HubSpot Abandoned Cart'
- 3Click the trigger slot labeled 'Add a trigger'
- 4Search for 'HTTP / Webhook' and select it
- 5Set the response type to 'Return a custom response' so Shopify's webhook delivery can receive a 200 OK
Shopify Admin > Settings > Notifications > Webhooks
Register the webhook in Shopify
In your Shopify admin, navigate to Settings > Notifications > Webhooks. Shopify natively supports an 'Abandoned checkouts / Checkout created' webhook event — this fires when a checkout is created but not completed after the customer has entered their email. Paste your Pipedream URL into the URL field, set the format to JSON, and save. Shopify will send a test ping to confirm the endpoint is reachable.
- 1Open Shopify Admin and go to Settings > Notifications
- 2Scroll to the bottom and click 'Create webhook'
- 3Set 'Event' to 'Checkout created'
- 4Paste your Pipedream webhook URL into the URL field
- 5Set format to JSON and click 'Save webhook'
Workflow Canvas > + Add Step > Run Node.js code
Validate the incoming Shopify payload
Add a Node.js code step immediately after the trigger to validate the webhook and confirm the checkout is genuinely abandoned. Shopify does not distinguish between an active checkout and an abandoned one in the webhook event name — that determination happens based on whether an order was completed. At this stage, extract the fields you need: customer email, first name, cart total, and the abandoned checkout URL. Log these to confirm the data shape before building downstream logic.
- 1Click '+ Add Step' below the trigger
- 2Select 'Run Node.js code'
- 3Paste the extraction code (see Pro Tip below)
- 4Click 'Test' to run the step against the Shopify test payload
Workflow Canvas > + Add Step > Delay
Add a delay to confirm abandonment
Shopify's webhook fires the moment a checkout is created, not after a proven abandonment window. Use Pipedream's built-in 'Delay' step to pause execution for 60 minutes. If the customer completes their purchase within that window, you'll want to cancel the enrollment — but Pipedream's standard delay doesn't support cancellation. The workaround: after the delay, query Shopify's Orders API to check whether an order exists for this checkout ID before proceeding.
- 1Click '+ Add Step' and search for 'Delay'
- 2Select the Pipedream 'Delay' built-in action
- 3Set the delay duration to 3600 seconds (60 minutes)
- 4Click 'Save and continue'
Workflow Canvas > + Add Step > Run Node.js code
Check Shopify Orders API to confirm no purchase
After the delay, add a Node.js code step that queries the Shopify Admin REST API to check whether an order was placed against this checkout token. Use the GET /admin/api/2024-01/orders.json?status=any endpoint and filter by the checkout token from the original payload. If an order exists, set a flag to skip HubSpot enrollment and exit the workflow cleanly using $.flow.exit().
- 1Click '+ Add Step' and select 'Run Node.js code'
- 2Connect your Shopify store credentials via the Connected Accounts panel on the right
- 3Write the Orders API check using the checkout token from step 3's output
- 4Call $.flow.exit('Order completed — skipping enrollment') if an order is found
Workflow Canvas > + Add Step > HubSpot > Search Contacts
Look up or create the HubSpot contact
Add the HubSpot app step to search for an existing contact by email. In Pipedream's step library, search for 'HubSpot' and choose 'Search Contacts'. Use the email address extracted in Step 3 as the search value. If the contact exists, retrieve their contact ID for the enrollment step. If no contact is found, add a second HubSpot step to create a new contact with the cart data before enrolling them.
- 1Click '+ Add Step' and search for 'HubSpot'
- 2Select 'Search Contacts' from the action list
- 3Connect your HubSpot account via Connected Accounts
- 4Set the search filter: property = 'email', value = steps.validate_payload.$return_value.email
- 5Add a conditional Node.js step: if contact not found, run 'Create Contact' HubSpot action before proceeding
Workflow Canvas > + Add Step > HubSpot > Update Contact
Update HubSpot contact properties with cart data
Before enrolling the contact in a sequence, update their HubSpot record with the abandoned cart details. Add a HubSpot 'Update Contact' step. Map the Shopify cart total to a custom HubSpot property (e.g., abandoned_cart_value), the checkout URL to abandoned_checkout_url, and the cart item count to abandoned_cart_item_count. These properties make the sequence emails personalizable with tokens like {{contact.abandoned_cart_value}}.
- 1Click '+ Add Step' and select HubSpot > Update Contact
- 2Set Contact ID to the ID returned in Step 6
- 3Add property: abandoned_cart_value = steps.validate_payload.$return_value.cartTotal
- 4Add property: abandoned_checkout_url = steps.validate_payload.$return_value.checkoutUrl
- 5Add property: abandoned_cart_item_count = steps.validate_payload.$return_value.itemCount
Workflow Canvas > + Add Step > Run Node.js code
Enroll the contact in a HubSpot sequence
HubSpot's Sequences API allows programmatic enrollment via a POST to /crm/v3/objects/contacts/{contactId}/associations or via the Engagements API. Pipedream does not have a pre-built 'Enroll in Sequence' action, so you'll write a Node.js code step that calls HubSpot's Sequence Enrollment endpoint directly. You need the sequence ID (find it in HubSpot > Sequences > select your sequence > the ID is in the URL) and the sender's userId to assign email ownership.
- 1Click '+ Add Step' and select 'Run Node.js code'
- 2Use the HubSpot connected account's OAuth token via auths.hubspot.oauth_access_token
- 3POST to https://api.hubapi.com/automation/v4/sequences/enrollments
- 4Set body: { contactId, sequenceId, senderId } where sequenceId is the numeric ID from the HubSpot Sequences URL
- 5Log the response status and enrollment ID for debugging
Workflow Canvas > Trigger Step > Response Settings
Return a 200 response to Shopify
Shopify expects a 200 OK response within 5 seconds of delivering a webhook. Because Pipedream's delay step pauses execution for 60 minutes, the initial webhook response must be sent immediately — before the delay. Go back to your HTTP trigger step and confirm 'Return a custom response' is enabled. Add a $.respond() call at the very beginning of your first code step to send the 200 immediately, decoupling the response from the downstream workflow execution.
- 1Click the HTTP trigger step at the top of the workflow
- 2Confirm 'Return a custom response' is toggled ON
- 3In your Step 3 Node.js code, add $.respond({ status: 200, body: 'received' }) as the first line of execution
- 4Click 'Deploy' to save the workflow
pipedream.com > Workflows > [Your Workflow] > Executions
Test end-to-end with a real abandoned checkout
In Shopify, create a test checkout by adding a product to a cart, entering a real email address at the checkout page, and then closing the tab without completing the purchase. In Pipedream, open the workflow's execution log and watch the event appear. After 60 minutes, confirm the workflow resumes, the Shopify Orders check passes, the HubSpot contact is updated, and the sequence enrollment fires. Check HubSpot for the contact's sequence status.
- 1Open your Shopify store, add a product to cart, begin checkout, enter a test email, then abandon the tab
- 2In Pipedream, click the 'Executions' tab on your workflow
- 3Watch for a new execution row to appear within 30 seconds
- 4After 60 minutes, confirm the execution resumes and all downstream steps show green checkmarks
- 5Open HubSpot and verify the contact record has updated cart properties and shows active sequence enrollment
Paste this code into the Node.js validation step (Step 3) and again into the Shopify Orders check step (Step 5). The first function extracts and normalizes cart data from the Shopify webhook. The second queries the Orders API and exits the workflow cleanly if a purchase was completed. Both use Pipedream's $.flow.exit() and the connected Shopify account token available via auths.shopify.
JavaScript — Code Step// Step 3: Extract and normalize Shopify abandoned cart data▸ Show code
// Step 3: Extract and normalize Shopify abandoned cart data
export default defineComponent({
async run({ steps, $ }) {... expand to see full code
// Step 3: Extract and normalize Shopify abandoned cart data
export default defineComponent({
async run({ steps, $ }) {
const body = steps.trigger.event.body;
// Immediately acknowledge the webhook so Shopify doesn't retry
await $.respond({ status: 200, body: 'received' });
const email = (body.email || '').toLowerCase().trim();
if (!email) {
await $.flow.exit('No email on checkout — skipping');
}
const cartData = {
checkoutId: body.id,
checkoutToken: body.token,
email,
firstName: body.billing_address?.first_name || body.shipping_address?.first_name || '',
lastName: body.billing_address?.last_name || body.shipping_address?.last_name || '',
cartTotal: parseFloat(body.total_price || '0').toFixed(2),
currency: body.currency || 'USD',
checkoutUrl: body.abandoned_checkout_url,
itemCount: (body.line_items || []).length,
createdAt: body.created_at,
};
console.log('Cart data extracted:', JSON.stringify(cartData, null, 2));
return cartData;
},
});
// Step 5: Check Shopify Orders API — skip if purchase completed
export default defineComponent({
props: {
shopify: {
type: 'app',
app: 'shopify',
},
},
async run({ steps, $ }) {
const { checkoutToken, email } = steps.validate_payload.$return_value;
const shop = this.shopify.$auth.shop_id;
const token = this.shopify.$auth.oauth_access_token;
const url = `https://${shop}.myshopify.com/admin/api/2024-01/orders.json` +
`?checkout_token=${checkoutToken}&status=any&limit=1`;
const response = await fetch(url, {
headers: {
'X-Shopify-Access-Token': token,
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Shopify Orders API error: ${response.status} ${await response.text()}`);
}
const { orders } = await response.json();
if (orders && orders.length > 0) {
console.log(`Order found for checkout token ${checkoutToken} — purchase completed, exiting.`);
await $.flow.exit('Purchase completed — skipping HubSpot enrollment');
}
console.log(`No order found for ${email} — proceeding to HubSpot enrollment.`);
return { proceed: true, email };
},
});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 developer and you need custom abandonment logic — like skipping enrollment for wholesale customers, branching sequences by cart value, or checking a second data source before firing HubSpot. Pipedream's instant webhook processing means the checkout event hits your workflow in under a second, and Node.js code steps let you call the Shopify Orders API mid-execution to verify a purchase hasn't been completed. If nobody on your team writes code, use Klaviyo instead — it has native Shopify abandoned cart triggers with zero setup and handles the abandonment window automatically.
Pipedream's pricing is credit-based. This workflow consumes roughly 4-6 credits per execution: one for the trigger, one for the delay, one for the Shopify Orders API call, one for the HubSpot contact lookup, one for the contact update, and one for the sequence enrollment. At 500 abandoned carts per month, you're burning approximately 2,500-3,000 credits monthly. Pipedream's free tier includes 2,500 credits/month — you'll hit that ceiling fast. The Basic plan at $29/month includes 20,000 credits, which covers roughly 3,300 carts/month comfortably. Make.com handles the same volume for $9/month on its Core plan with no credit counting. Zapier would cost $49-$73/month for the same volume on a Professional plan.
Zapier has a cleaner UI for setting up the HubSpot sequence enrollment step and handles the Shopify trigger with zero code — setup takes 15 minutes versus 2-3 hours on Pipedream. But Zapier can't do the mid-execution Shopify Orders check without a separate Zap and a Zapier Storage lookup, which is fragile. Make.com has a native 'Sleep' module that handles the abandonment delay elegantly and its HTTP module calls the Shopify Orders API cleanly — but Make has a known latency issue where webhook processing can take 30-90 seconds on the free tier, which matters if you're competing for customer attention. n8n gives you the same Node.js flexibility as Pipedream for free if you self-host, but you're managing infrastructure. Power Automate doesn't have a native Shopify connector — you'd use the HTTP action for every Shopify call, which makes the workflow brittle. Pipedream is the right call here specifically because the Orders API check mid-execution is hard to replicate cleanly anywhere else.
Three things you'll hit after launch. First: Shopify fires 'Checkout created' for every checkout, including ones where the customer eventually completes the purchase during your delay window — your Orders API check is the only thing preventing enrolled paying customers from getting 'you forgot something' emails. Test this case explicitly before going live. Second: HubSpot rate-limits the Sequences enrollment API to 10 requests per second and 1,000 per day on Sales Hub Starter. At high cart abandonment volume, you can hit the daily cap — monitor the HubSpot API usage dashboard and upgrade to Sales Hub Professional if needed. Third: Shopify's abandoned_checkout_url expires after 24 hours if not recovered. If your sequence sends a recovery email more than 24 hours after abandonment, the link in the email returns a 404. Either keep your first sequence email within the 24-hour window or regenerate the URL via Shopify's API before sending.
Ideas for what to build next
- →Add cart value branching — Split the workflow at Step 8 to enroll high-value carts (>$150) in a premium sequence with a discount offer, and low-value carts in a simpler 2-email reminder sequence. Add a Pipedream 'Filter' step that checks steps.validate_payload.$return_value.cartTotal and routes to the correct HubSpot sequence ID.
- →Sync sequence reply data back to Shopify — Add a second Pipedream workflow triggered by HubSpot's 'Sequence Enrolled Contact Replied' webhook. When a customer replies to the recovery email, update a Shopify customer metafield to flag them as 'email engaged' — useful for excluding them from Shopify-native discount campaigns.
- →Add Slack alerting for high-value abandonments — After the HubSpot enrollment step, add a Slack step that posts to your #ecommerce channel whenever a cart over $500 is abandoned. Include the customer name, cart total, and the HubSpot contact URL so your team can do manual outreach alongside the automated sequence.
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