How it works

Follow these 4 simple steps to track Shopify refunds in Mixpanel automatically

Shopify logo icon

Refund Created

App connector: Shopify • Time to complete: 0 minutes (Auto-configured)
Why this matters: This trigger captures refunds the instant they're issued, providing immediate visibility into return activity and enabling real-time dashboards that track refund volume, amounts, and patterns as they happen.

When a refund is created in Shopify, this trigger activates the workflow and captures refund data including {{shopify.order_id}}, {{shopify.created_at}}, {{shopify.transactions}}, {{shopify.refund_line_items}}, and {{shopify.order_adjustments[0].reason}}. The workflow runs for every refund issued in your store, whether full or partial refunds.

Retrieve Order

App connector: Shopify • Time to complete: 0 minutes (Auto-configured)
Why this matters: Fetches the complete order record to provide context for the refund, including customer details, order name, and full line item information that's necessary for matching refunded items to their SKUs and product data.

This step retrieves the full order record from Shopify using {{shopify.order_id}} from the trigger. It fetches {{shopify_1.id}}, {{shopify_1.name}}, {{shopify_1.email}}, {{shopify_1.customer.first_name}}, {{shopify_1.customer.last_name}}, and the complete {{shopify_1.line_items}} array. This order data is essential because the refund payload only contains refunded line item IDs and quantities, not the full product details like SKUs and titles that are needed for meaningful analytics.

Custom Code

App connector: Code • Time to complete: 0 minutes (Auto-configured)
Why this matters: Transforms raw refund and order data into analytics-ready metrics by calculating total refund amount, formatting product lists, matching SKUs, and summing quantities—work that would be difficult to accomplish with standard workflow steps.

This custom JavaScript step processes the refund and order data to create formatted outputs stored as {{custom}}. The code performs four key calculations:
(1) Loops through {{shopify.transactions}} to calculate the total refund amount (stored as {{custom.total_refund_amount}}),
(2) Extracts and formats product titles from {{shopify.refund_line_items}} into a comma-separated list (stored as {{custom.refunded_line_items_titles}}),
(3) Sums the quantities from {{shopify.refund_line_items}} to get total items refunded (stored as {{custom.refunded_line_items_total_quantity}}), and
(4) Matches refunded line item IDs to the full order's line items from {{shopify_1.line_items}} to extract SKUs and format them as a comma-separated list (stored as {{custom.refunded_line_items_skus}}).

Custom code:
const Mesa = require('vendor/Mesa.js');
module.exports = new (class {
script = (prevResponse, context) => {
const vars = context.steps;
let response = {};

const refundPayload = vars.shopify;
const shopifyOrder = vars.shopify_1;

// Calculate total refund amount from transactions
let refundAmount = 0;
refundPayload.transactions.forEach(transaction => {
refundAmount -= transaction.amount;
});

// Format refunded line items' titles to comma-separated list
const refundedLineItemsTitles = refundPayload.refund_line_items
.map(item => item.line_item.title)
.join(', ');

// Sum total quantity of refunded line items
const refundedLineItemsTotalQuantity = refundPayload.refund_line_items
.reduce((sum, item) => sum + item.quantity, 0);

// Create lookup object for line items by ID
const lineItemsById = {};
shopifyOrder.line_items.forEach(item => {
lineItemsById[item.id] = item;
});

// Extract SKUs for matching refunded line items
const refundedLineItemsSkus = refundPayload.refund_line_items
.map(refundItem => {
const match = lineItemsById[refundItem.line_item_id];
return match ? match.sku : null;
})
.filter(Boolean)
.join(",");

response.total_refund_amount = refundAmount;
response.refunded_line_items_titles = refundedLineItemsTitles;
response.refunded_line_items_total_quantity = refundedLineItemsTotalQuantity;
response.refunded_line_items_skus = refundedLineItemsSkus;

Mesa.output.next(response);
};
})();

Mixpanel logo icon

Create an Event

App connector: Mixpanel • Time to complete: 1 minute
Why this matters: Creates a rich refund event in Mixpanel with comprehensive properties that enable analysis of refund patterns by product, customer, amount, reason, and timing—insights critical for reducing returns and improving customer satisfaction.

This step sends a refund event to Mixpanel with the event name formatted as "Refund - Order {{shopify_1.name}}" (like "Refund - Order #1234"). The event includes extensive properties:

Order ID ({{shopify_1.id}}),
Order Name ({{shopify_1.name}}),
Refund Amount ({{custom.total_refund_amount}}),
Refund Timestamp ({{shopify.created_at}}),
Refund Reason ({{shopify.order_adjustments[0].reason}}),
Refunded Line Items Titles ({{custom.refunded_line_items_titles}}),
Refunded Line Items Total Quantity ({{custom.refunded_line_items_total_quantity}}),
Refunded Line Items SKU ({{custom.refunded_line_items_skus}}),
Billing Customer Name ({{shopify_1.customer.first_name}} {{shopify_1.customer.last_name}}), and
Billing Customer Email ({{shopify_1.email}}).

The query includes project_id and strict validation for data quality. All properties are ready to use in Mixpanel reports, cohorts, and dashboards without modification.

Make it your own

Customize this workflow even further:

Add product-level refund details
Modify the custom code to create separate events for each refunded line item instead of one aggregated event, enabling product-specific refund rate analysis and identifying which products drive the most returns.
Include refund method information
Add properties to the Mixpanel event that capture the refund gateway, payment method, or whether it was a store credit versus original payment method refund, helping analyze refund processing patterns.
Send alerts for high-value refunds
Add a filter step before Mixpanel that checks {{custom.total_refund_amount}} and sends Slack notifications when refunds exceed a certain threshold, enabling immediate investigation of significant returns.
Track time-to-refund metrics
Modify the custom code to calculate the days between order creation ({{shopify_1.created_at}}) and refund creation ({{shopify.created_at}}), adding this as a property to analyze how quickly refunds are processed.

Frequently asked questions

What if a refund doesn't have a reason specified?
The Refund Reason property ({{shopify.order_adjustments[0].reason}}) will be empty if no reason was entered when processing the refund. The event will still be created with all other properties populated. Consider adding a filter or default value in the custom code if you want to ensure every event has a reason.
Can I track partial refunds versus full refunds differently?
Yes, add a comparison in the custom code that checks if {{custom.total_refund_amount}} equals the order's {{shopify_1.total_price}} to determine full versus partial refunds, then add this as a property or create different event names for each type.
How are refund amounts calculated when there are multiple transactions?
The custom code loops through all transactions in {{shopify.transactions}} and sums the amounts. The code negates the amounts because Shopify stores refund transaction amounts as positive numbers, but they represent money leaving your account, so the code converts them to negative values for accurate refund total calculation.
What is a template?
Templates are pre-made workflows by our team of experts. Instead of building a workflow from scratch, these have all the steps needed to complete the task.
Can I personalize a template?
Yes! Every step can be customized to meet your exact requirements. Additionally, you can even add more steps and make it more sophisticated.
Are templates free?
Yes! Our entire library containing hundreds of templates are free to use and customize to your exact needs.

Ready to track Shopify refunds in Mixpanel automatically?

Join thousands who've automated their work and saved an average of 3.5 hours every week.

Start with this template — It's free
7-day free trial • 4 min setup • Cancel anytime