◈Home ▸Blog ◇Contact
Home/Blog/Duplicate Shopify Customers Break Retention M…
How toEcommerceShopifyRetentionData Quality

Duplicate Shopify Customers Break Retention Math

9 min readBy Miloš Mitrović

If your repeat purchase rate has been drifting down while average order value and revenue hold steady, check the customer table before you rebuild the retention program. Duplicate Shopify customer records split one buyer's order history across two or more identities, which inflates the first time buyer count and deflates every metric that carries customers in the denominator. The first move is not a bulk merge. It is a measurement pass that tells you how much of your retention picture is an artefact of identity resolution rather than customer behaviour.

Key takeaways

  • Duplicates bias metrics in a predictable direction: new customer counts go up, repeat rate and orders per customer go down, and lifetime value looks flatter than it is.
  • Most duplicates come from four sources: guest checkout without an account, email casing and plus addressing, phone only records from POS and SMS, and marketplace or wholesale imports.
  • Measure the duplicate rate on a normalised email key before you merge anything, because the merge decision depends on how much revenue sits in the affected records.
  • Email normalisation is provider specific. Lowercasing is safe everywhere, stripping plus tags and dots is only safe for the providers that document that behaviour.
  • Klaviyo inherits the split identity, so duplicates also misroute welcome, winback and replenishment flows, not only reporting.
  • For most stores the correct sequence is: build a canonical customer key in the reporting layer first, merge the high value duplicates second, fix the intake path third.

Which numbers move when a buyer has two records

The metrics that break are the ones with a customer count in the denominator, and they all break in the same direction. A buyer with four orders across two records is counted as two customers with two orders each. Nothing about your revenue changes, which is why this never shows up in a finance review and can sit undetected for years.

MetricDirectionMechanism
New customersOverstatedThe second record has no prior order history, so it registers as a first time buyer.
Repeat purchase rateUnderstatedOrders are divided across identities, so fewer records cross the two order threshold.
Orders per customerUnderstatedSame order count over a larger customer base.
Lifetime value by cohortUnderstated and flattenedLater orders land on a record that belongs to a later cohort.
Time to second orderOverstatedSecond orders that landed on a duplicate record never close the gap.
Customer acquisition cost paybackOverstatedRepeat revenue is attributed to a customer you count as newly acquired.

The practical consequence is that a store with a 12 percent duplicate rate concentrated in its best buyers will look like it has a retention problem it does not have. Teams then spend a quarter rebuilding flows to fix a measurement defect.

Where Shopify creates the second record

Duplicates almost always come from an intake path, not from customers behaving strangely. Shopify creates or attaches a customer record at checkout regardless of whether the shopper logs in, and the matching logic it applies at that moment is the source of most of the damage.

  • Guest checkout. Stores that do not require accounts get a customer record per checkout, matched on the email entered at that checkout. Requiring accounts removes the problem and costs you conversion, which is why most stores keep guest checkout (account creation is a documented driver of cart abandonment in Baymard's abandonment research). Shopify's own customer accounts documentation covers which account mode you are running.
  • Email variants. Casing differences, plus addressing, and the personal versus work address split all produce distinct records.
  • Phone only identities. POS orders and SMS signups can create records with a phone and no email. These never collide with the ecommerce record, so a retail buyer and the same person online are two customers forever.
  • Channel and app imports. Marketplace orders, wholesale channels, subscription apps and CSV imports write customers through the Admin API. Any app that creates rather than searches first will produce a duplicate. The Customer object reference shows what those integrations can and cannot match on.

Before you fix anything, work out which of these four is producing volume today. A store with a decade of history and a clean current intake path has an archive problem. A store creating fifty duplicates a week has an integration problem, and merging first just means merging again next month.

How to size the duplicate rate before you change anything

Export the customer table and group it on a normalised email key, then weight the result by revenue rather than record count. Record count tells you how messy the table is. Revenue weight tells you whether it is worth doing anything about.

SELECT lower(trim(email)) AS norm_email,
       count(*)           AS records,
       sum(orders_count)  AS orders,
       sum(total_spent)   AS spend
FROM shopify_customers
WHERE email IS NOT NULL
GROUP BY 1
HAVING count(*) > 1
ORDER BY spend DESC;

Run three passes and keep them separate, because each has a different false positive profile:

  1. Normalised email. Lowercase and trimmed. Near zero false positives. This is the set you can act on with confidence.
  2. Phone in E.164 format. Good for POS and SMS records. Watch for shared household numbers and for staff test records that carry a store phone number.
  3. Last name plus normalised shipping address plus no email collision. The highest yield pass and the one that produces the most false positives, because households share addresses. Treat this pass as a review queue, never as an automatic merge.

The number you want at the end is: what share of orders placed in the last 24 months belong to a customer who also has at least one other record. If that number is under two percent, fix the intake path and move on. If it is over eight percent, your retention reporting is not describing your business and nothing downstream of it should be trusted until this is resolved. The same weighting discipline applies once your order table gets large enough that these queries stop being cheap, which is covered in retention reporting past a million orders.

Why email normalisation is a judgement call

Lowercasing is always safe, but stripping plus tags and dots is only safe for providers that document ignoring them. The standard is explicit that the local part of an address is the business of the receiving host: RFC 5321 section 2.3.11 requires domains to be treated case insensitively and leaves local part interpretation to the destination server. In practice every major consumer provider lowercases, so lowercasing costs you nothing.

Plus addressing and dots are different. Gmail ignores dots in the local part and everything after a plus sign, so [email protected] and [email protected] reach the same inbox. Many corporate and self hosted domains do not behave that way, and a few treat the plus tag as part of a distinct mailbox. So the normalisation rule has to be conditional on the domain:

  • Always: trim whitespace, lowercase the whole address.
  • Gmail and Googlemail domains only: strip dots from the local part, strip everything from the first plus sign.
  • Other domains: strip the plus tag for grouping and review, but do not merge on it without a second signal such as a matching shipping address or phone.

Getting this wrong in the aggressive direction merges two people. That is worse than leaving the duplicate in place, because a merge in Shopify is not reversible.

How the split identity reaches Klaviyo

Klaviyo resolves profiles primarily on email, so two Shopify customers with different addresses become two Klaviyo profiles carrying separate order histories. Klaviyo's profile identifier documentation describes the matching order, and the consequence is that a buyer's second identity looks brand new to every flow you have running.

What that produces in an account:

This is the part that costs money directly rather than costing you an accurate dashboard. A discount led welcome email sent to a loyal buyer is margin given away to someone who was going to buy anyway.

The canonical key that fixes reporting without touching Shopify

You can correct every retention metric without merging a single record by resolving identity in the reporting layer instead of the source system. Build a customer identity table that maps every Shopify customer id to a canonical person id, generated from the normalised email pass plus a reviewed phone and address pass. Join orders to the canonical id rather than to customer_id, and recompute repeat rate, cohorts and lifetime value on that key.

Two things make this worth doing before any merge. It is reversible, so a bad match costs you a rerun rather than a destroyed record. And it gives you the counterfactual: run the same report on both keys and the gap between them is the exact size of your identity problem, which is the number that justifies the cleanup work to whoever has to approve it. Where that identity table lives is the same question covered in tags, metafields or a warehouse, and the answer is usually the warehouse, because this key needs to join to orders at query time.

Trade-offs and what I would do

There are three responses available and they are not alternatives, they are a sequence. Choosing only one is where most teams go wrong.

Merging in Shopify is the only option that fixes flows, segments and customer service views as well as reporting, because it corrects the source of truth everything else reads from. It is also permanent. Shopify's customerMerge mutation documents which records are eligible and which are blocked, including restrictions around subscriptions, gift cards and company contacts, and the operation cannot be undone. The manual path through the admin is covered in Shopify's customer management documentation, and the operational detail of doing this at volume is in finding and merging duplicate Shopify customers.

Keying around it in the warehouse fixes reporting in days, carries no risk, and fixes nothing that customers experience. Your flows keep misfiring.

Fixing the intake path stops new duplicates and does nothing about the archive. On its own it looks like inaction for a quarter.

My recommendation for a store with a meaningful duplicate rate: build the canonical key first, because it is cheap, reversible and it sizes the problem in revenue terms within a week. Then merge only the exact email match set, which is the pass with effectively no false positives, and only for customers with two or more orders, which is where the reporting and flow damage is concentrated. Leave the fuzzy address matches in a review queue and work them by hand if the revenue justifies it. Last, close the intake path that is generating volume, usually an app or channel integration that creates customers instead of searching first.

The one case where I would reverse that order is a store creating duplicates faster than it can merge them. If the weekly duplicate creation rate is climbing, fix the integration before you clean anything, because otherwise you are merging the same customer twice a year and paying for it in staff time every time.

Sources

M
Miloš Mitrović
Email Marketing for Ecommerce

Have a question or a project?

Whether it is about this post or a system you want built, I'm happy to talk.

Get in touch

404

Post not found. It may have been moved or the link is incorrect.

← Back to the blog
Summarize with AI
ChatGPT, Perplexity, and Grok open with the prompt ready to run. Claude, Gemini, and Copilot open a chat with the prompt copied; press Ctrl+V (Cmd+V on Mac) to paste. The full text is included, so it works even without web access.