Migrating a large customer base from a legacy system into Shopify requires a four-step approach: validate and map the source data to Shopify's schema, batch-import customers with tags and metafields, transfer loyalty balances to the target system, and audit the result to catch drift or missing records before going live. Each step reduces risk and makes the handoff verifiable.
A mid-market DTC merchant needed to migrate tens of thousands of customer records from a legacy CRM into Shopify while preserving loyalty balances, lifetime value metadata, and existing tag-based audience segmentation. The challenge was maintaining data integrity across format differences, handling duplicate detection, and ensuring loyalty data remained queryable post-migration.
Four pieces
Migration
Data Mapper & Validator
Reads a CRM export file, checks that emails and addresses match Shopify's format requirements, flags duplicates and malformed records, and produces a clean file ready for bulk import. Validation catches upstream issues before they create customer records.
Changed CustomerCreateInput to CustomerInput per Admin API schema.
Migration
Bulk Import Job
Processes the validated customer file in batches, creates or updates customer records in Shopify, applies segmentation tags, and stores loyalty balances as metafields. Built as a background job to handle high volume without blocking the admin.
Fixed: customerCreate takes single CustomerInput, not array. For batch processing, loop this mutation per customer in backend job queue.
Operations
Loyalty Points Transfer
Moves or recreates loyalty point balances from the CRM into the target loyalty system—whether Shopify Subscriptions, a third-party app such as Smile or Swell, or a custom points ledger. Balances become queryable and actionable in the new system.
Integration service + app API
Admin GraphQL explorergraphql
# Admin GraphQL — store loyalty balance as metafield for retrieval later
mutation SetLoyaltyBalance($input: MetafieldsSetInput!) {
metafieldsSet(metafields: [$input]) {
metafields {
id
namespace
key
value
}
userErrors { field message }
}
}
# Variables:
# {
# "input": {
# "ownerId": "gid://shopify/Customer/12345",
# "namespace": "loyalty",
# "key": "points_balance",
# "type": "integer",
# "value": "1250"
# }
# }
If using Smile or Swell, replace with their respective APIs; this stores raw balance in Shopify for now.
Operations
Post-Import Audit Dashboard
Scans imported customers to verify count, tag distribution, metafield completeness, and loyalty balance totals. Surfaces failed records, validation errors, and drift between the CRM export and live Shopify data before going live.
Custom admin dashboard
Admin GraphQL explorergraphql
# Admin GraphQL — count imported customers and fetch sample metafields
query AuditImport {
customers(first: 250, query: "created:>2024-01-01") {
edges {
node {
id
email
tags
createdAt
metafields(first: 10) {
edges {
node {
namespace
key
value
}
}
}
}
}
pageInfo { hasNextPage endCursor }
}
shop { name }
}
# Run this query to verify customer count, tags applied, and loyalty metafields synced.
Paginate with cursor to scan all 50K records; build a summary report offline.
Got a similar problem?
Sketch your build in 30 seconds — voice, type, or attach a screenshot.