A customer places an order on Shopify. The CRM does not update for 4 hours. A sales rep calls to upsell based on the previous order history, unaware that the customer just spent $800 on a competitor product. The revenue opportunity is gone. The customer experience is worse than no outreach at all.
Shopify CRM synchronization is the architectural discipline of keeping customer records, order history, contact attributes, and behavioral signals consistent between Shopify’s commerce data and a CRM’s sales and marketing data in near real-time, without duplicating records, overwriting manual updates, or creating data loops between the two systems.
This guide covers the complete CRM data sync Shopify architecture: strategy selection by use case, customer identity resolution to prevent duplicate CRM contacts, field mapping from Shopify objects to CRM records, bidirectional sync conflict management, Salesforce-specific implementation patterns, and observability for production CRM integrations.
What Is Shopify CRM Synchronization?
Shopify CRM synchronization is the continuous or scheduled process of propagating customer data, order events, and behavioral signals from Shopify to a CRM system, and selectively pushing CRM data such as contact enrichment, lifecycle stage updates, and sales rep assignments back to Shopify.
The asymmetry in that definition is intentional. Shopify is the source of truth for transactional data: orders, refunds, cart activity, product interactions. The CRM is the source of truth for relationship data: contact records, lifecycle stages, deal history, communication logs. A well-designed sync architecture respects these boundaries rather than attempting to mirror all data in both directions.
Three failure modes recur in poorly designed Shopify CRM sync implementations. The first is duplicate contact records: a customer who places two orders on different email addresses, or who exists both as a lead and a contact in the CRM, gets two disconnected records that fragment their history. The second is data overwrite loss: a sales rep manually updates a CRM contact’s phone number, which the next sync cycle overwrites with Shopify’s stale value. The third is sync lag visibility: the integration appears to be running but is silently falling behind, and the CRM shows customer data that is hours or days stale.
The fault-tolerant Shopify integration design principles that govern all Shopify integrations apply with particular force in CRM contexts because CRM data errors affect sales rep decisions, marketing campaign targeting, and customer service quality in ways that are difficult to detect and expensive to correct.
CRM Synchronization Strategy Selection
No single synchronization strategy suits every CRM data domain. Order events require near real-time push. Contact enrichment tolerates batch sync. High-volume customer attribute updates benefit from change data capture. Choosing the right strategy per domain reduces both infrastructure cost and data freshness risk.
| Strategy | Trigger | Latency | Best For | Key Risk |
| Real-time webhook push | Shopify event | Seconds | Order and customer events | Duplicate records without dedup |
| Scheduled batch sync | Cron schedule | Minutes to hours | Contact enrichment, bulk updates | Stale data between cycles |
| Event-driven middleware | Domain event bus | Near real-time | Multi-CRM fan-out | Event ordering complexity |
| CRM-initiated polling | CRM cron or trigger | Minutes | Legacy CRM systems | API rate limit exhaustion |
| Change data capture | DB transaction log | Seconds | High-volume customer updates | CDC infrastructure overhead |
Most production Shopify CRM integrations combine two or three strategies. Real-time webhook push handles transactional events. Scheduled batch sync handles enrichment and bulk attribute updates. The combination gives you freshness where it matters and efficiency where real-time is unnecessary.
Pair strategy selection with async Shopify architecture patterns to ensure webhook-driven CRM updates never block the synchronous request path and always have explicit retry and dead-letter handling.
Customer Identity Resolution for CRM Sync
Customer identity resolution is the most technically complex problem in Shopify CRM synchronization. A single customer may place guest orders using three different email addresses, create an account under a fourth, and exist in the CRM as both a lead and a contact created by the sales team. Without identity resolution, every sync event creates a new CRM record, fragmenting the customer’s history across multiple disconnected contacts.
Identity Resolution Strategies
Three identity resolution strategies apply in Shopify CRM integrations, in order of increasing sophistication:
- Email-based matching: the simplest strategy. Before creating a new CRM contact, query the CRM for an existing record matching the Shopify customer’s email address. If found, update it. If not found, create it. Fails for customers who use different email addresses across orders.
- Phone-based fallback: extend email matching with a phone number fallback. If no email match exists, query for a phone match. Useful for B2B customers who may use departmental email aliases for orders but have a consistent phone number in the CRM.
- Probabilistic matching: match on combinations of name, address, phone, and email using a scoring algorithm. Appropriate for large B2C merchants where guest checkout creates many non-email-unique customer records. Requires a matching service or a CRM with built-in duplicate detection (Salesforce Duplicate Management, HubSpot duplicate contact detection).
// Customer identity resolution: email-first with phone fallback
async function resolveOrCreateCRMContact(crmClient, shopifyCustomer) {
const email = shopifyCustomer.email?.toLowerCase().trim();
const phone = shopifyCustomer.phone?.replace(/\D/g, ''); // Normalize phone
// Step 1: Exact email match
if (email) {
const byEmail = await crmClient.contacts.search({ email });
if (byEmail.length === 1) return { contact: byEmail[0], matched: 'email' };
if (byEmail.length > 1) {
// Multiple matches: flag for manual deduplication review
await flagForDeduplication(shopifyCustomer, byEmail);
return { contact: byEmail[0], matched: 'email_ambiguous' };
}
}
// Step 2: Phone fallback
if (phone) {
const byPhone = await crmClient.contacts.search({ phone });
if (byPhone.length === 1) return { contact: byPhone[0], matched: 'phone' };
}
// Step 3: No match — create new contact
const newContact = await crmClient.contacts.create({
email: shopifyCustomer.email,
phone: shopifyCustomer.phone,
firstName: shopifyCustomer.first_name,
lastName: shopifyCustomer.last_name,
source: 'shopify',
shopifyId: shopifyCustomer.id.toString(),
});
return { contact: newContact, matched: 'created' };
}
|
Store the CRM contact ID against the Shopify customer ID in your middleware database immediately after resolution. All subsequent sync events for that customer use the stored mapping rather than re-running the resolution query, reducing CRM API calls and preventing re-resolution from creating duplicate records if the original contact’s email changes in the CRM.
Field Mapping: Shopify Customer and Order Data to CRM
CRM field mapping defines which Shopify data properties map to which CRM record fields, and which transformation or normalization rules apply. Poor field mapping is the most common cause of CRM data quality problems in Shopify integrations.
Shopify Customer to CRM Contact Mapping
// Field mapping: Shopify Customer -> CRM Contact
// Adjust CRM field names to match your specific CRM's API schema
function mapShopifyCustomerToCRM(shopifyCustomer, existingCRMContact = null) {
const mapped = {
// Standard contact fields
email: shopifyCustomer.email,
firstName: shopifyCustomer.first_name,
lastName: shopifyCustomer.last_name,
phone: shopifyCustomer.phone,
// Address — only update if Shopify has a value
// Prevents overwriting CRM-enriched address with Shopify's null
...(shopifyCustomer.default_address && {
address: shopifyCustomer.default_address.address1,
city: shopifyCustomer.default_address.city,
state: shopifyCustomer.default_address.province_code,
country: shopifyCustomer.default_address.country_code,
zip: shopifyCustomer.default_address.zip,
company: shopifyCustomer.default_address.company,
}),
// Commerce-specific CRM fields (custom properties)
shopifyCustomerId: shopifyCustomer.id.toString(),
shopifyCreatedAt: shopifyCustomer.created_at,
totalSpend: parseFloat(shopifyCustomer.total_spent),
orderCount: shopifyCustomer.orders_count,
averageOrderValue: shopifyCustomer.orders_count > 0
? parseFloat(shopifyCustomer.total_spent) / shopifyCustomer.orders_count
: 0,
acceptsMarketing: shopifyCustomer.email_marketing_consent?.state === 'subscribed',
customerTags: shopifyCustomer.tags,
verifiedEmail: shopifyCustomer.verified_email,
};
// Selective update: skip CRM-owned fields if contact already exists
// Prevents sync from overwriting manual CRM enrichment
if (existingCRMContact) {
const CRM_OWNED_FIELDS = ['lifecycleStage', 'leadSource', 'ownerId', 'notes'];
CRM_OWNED_FIELDS.forEach(field => delete mapped[field]);
}
return mapped;
}
|
Shopify Order to CRM Deal or Activity Mapping
How Shopify orders map to CRM records depends on your CRM’s data model and your sales process. In Salesforce, orders typically map to Opportunities (for B2B with a sales cycle) or Orders (for B2C transactional records). In HubSpot, orders map to Deals. In Pipedrive, to Activities or Deals depending on configuration.
The critical mapping decision is whether every Shopify order creates a new CRM record or whether orders aggregate into a running customer lifetime value property on the contact record. For B2C merchants with high order frequency, creating a CRM deal per order generates enormous record volumes that degrade CRM performance. Aggregating order metrics onto the contact record (total spend, order count, last order date, average order value) is more practical and equally useful for segmentation and outreach.
Shopify Salesforce Sync: Architecture and Implementation
Shopify Salesforce sync is the most commonly requested and most technically complex CRM integration in the Shopify ecosystem. Salesforce’s object model — Leads, Contacts, Accounts, Opportunities, Orders — does not map cleanly to Shopify’s customer and order model, requiring explicit architectural decisions about which Salesforce objects receive which Shopify data.
Salesforce Object Model for Shopify Data
The recommended Salesforce object mapping for Shopify B2C merchants is:
- Shopify Customer -> Salesforce Contact: B2C customers map to Contacts directly, not Leads. Leads are for unqualified prospects. A customer who has placed an order is a qualified contact.
- Shopify Order -> Salesforce Order: use Salesforce’s native Order object with OrderItems for line items. Link each Order to the corresponding Contact via the Account relationship.
- Shopify Customer (B2B company) -> Salesforce Account + Contact: for B2B merchants, the company maps to a Salesforce Account and the individual contact maps to a Contact associated with that Account.
// Shopify Salesforce sync: upsert Contact using Shopify customer ID
// Uses Salesforce REST API with external ID field for idempotent upsert
async function upsertSalesforceContact(sfClient, shopifyCustomer) {
// Shopify_Customer_ID__c: custom external ID field on Contact object
// Enables idempotent upsert — Salesforce handles insert vs update
const contactPayload = {
FirstName: shopifyCustomer.first_name,
LastName: shopifyCustomer.last_name || 'Unknown',
Email: shopifyCustomer.email,
Phone: shopifyCustomer.phone,
MailingStreet: shopifyCustomer.default_address?.address1,
MailingCity: shopifyCustomer.default_address?.city,
MailingState: shopifyCustomer.default_address?.province_code,
MailingCountryCode: shopifyCustomer.default_address?.country_code,
MailingPostalCode: shopifyCustomer.default_address?.zip,
// Custom fields on Contact object (create in Salesforce Setup)
Shopify_Customer_ID__c: shopifyCustomer.id.toString(),
Total_Spend__c: parseFloat(shopifyCustomer.total_spent),
Order_Count__c: shopifyCustomer.orders_count,
Last_Order_Date__c: shopifyCustomer.last_order_id ? new Date().toISOString() : null,
Accepts_Marketing__c: shopifyCustomer.email_marketing_consent?.state === 'subscribed',
};
// PATCH to external ID field: Salesforce inserts if not exists, updates if exists
await sfClient.sobject('Contact').upsert(
contactPayload,
'Shopify_Customer_ID__c' // External ID field name
);
}
// Shopify Order -> Salesforce Order with OrderItems
async function createSalesforceOrder(sfClient, shopifyOrder, sfAccountId) {
const sfOrder = await sfClient.sobject('Order').create({
AccountId: sfAccountId,
EffectiveDate: shopifyOrder.created_at.split('T')[0],
Status: 'Draft',
Shopify_Order_ID__c: shopifyOrder.id.toString(),
Shopify_Order_Name__c: shopifyOrder.name,
TotalAmount: parseFloat(shopifyOrder.total_price),
CurrencyIsoCode: shopifyOrder.currency,
});
// Create OrderItems for each line item
await sfClient.sobject('OrderItem').createBulk(
shopifyOrder.line_items.map(item => ({
OrderId: sfOrder.id,
Quantity: item.quantity,
UnitPrice: parseFloat(item.price),
Shopify_SKU__c: item.sku,
Description: item.title,
}))
);
return sfOrder.id;
}
|
The external ID upsert pattern on Shopify_Customer_ID__c is critical for idempotency in Salesforce sync. It tells Salesforce to insert a new Contact if no record with that Shopify customer ID exists, or update the existing one if it does. This replaces the query-then-insert-or-update pattern, reducing API calls from 2 to 1 per customer sync operation and eliminating the race condition where two concurrent syncs both query, find no match, and both create a new record.
Bidirectional CRM Sync: Conflict Management
Bidirectional CRM data sync Shopify creates the same conflict and sync loop risks that ERP integrations face, but with additional complexity: CRM users actively edit contact records while integrations are running. A sales rep updating a customer’s lifecycle stage, adding a note, or correcting a phone number must not be overwritten by the next Shopify sync cycle.
Field-Level Sync Ownership
The most reliable conflict management strategy for CRM sync is field-level sync ownership: define which fields the integration is permitted to write and which fields the CRM owns exclusively. The integration never writes to CRM-owned fields, regardless of what Shopify’s data contains.
// Field-level sync ownership configuration
// Integration only writes fields it owns — never overwrites CRM-managed fields
const FIELD_OWNERSHIP = {
// Integration-owned: always sync from Shopify
integration: [
'shopifyCustomerId', 'totalSpend', 'orderCount',
'lastOrderDate', 'averageOrderValue', 'acceptsMarketing',
'customerTags', 'shopifyCreatedAt',
],
// CRM-owned: never overwrite, even if Shopify has a value
crm: [
'lifecycleStage', 'leadStatus', 'ownerId',
'dealStage', 'notes', 'customSegments',
],
// Shopify-wins on create, CRM-wins on update
shopifyOnCreate: [
'firstName', 'lastName', 'email', 'phone',
'address', 'city', 'state', 'country', 'zip',
],
};
function buildSyncPayload(shopifyData, existingCRMRecord, isCreate) {
const payload = {};
// Always write integration-owned fields
FIELD_OWNERSHIP.integration.forEach(field => {
if (shopifyData[field] !== undefined) payload[field] = shopifyData[field];
});
// Write shopifyOnCreate fields only on new record creation
if (isCreate) {
FIELD_OWNERSHIP.shopifyOnCreate.forEach(field => {
if (shopifyData[field] !== undefined) payload[field] = shopifyData[field];
});
}
// Never write CRM-owned fields
// FIELD_OWNERSHIP.crm fields are never touched
return payload;
}
|
Preventing CRM Sync Loops
CRM-to-Shopify sync loops occur when a CRM webhook or API call updates a Shopify customer record, which triggers a Shopify customers/update webhook, which the integration ingests and syncs back to the CRM, which triggers another CRM update. Tag every Shopify write made by the integration middleware with a metafield marking its origin. Check this tag in every webhook handler before routing to the CRM.
This is the same sync loop prevention pattern used in ERP integrations. For detailed implementation, the fault-tolerant Shopify integration guide covers the origin tagging approach with full middleware code.
Marketing Consent and GDPR Compliance in CRM Sync
Marketing consent data is one of the most legally sensitive fields in any Shopify CRM synchronization implementation. GDPR, CASL, and CAN-SPAM require that marketing consent be accurately reflected in your CRM’s email marketing lists. A sync that incorrectly marks an opted-out customer as subscribed in the CRM causes illegal marketing communications. A sync that marks an opted-in customer as unsubscribed causes unnecessary revenue loss.
Shopify Consent State to CRM Mapping
Shopify’s email_marketing_consent object (introduced in API version 2022-04) provides granular consent state beyond the deprecated accepts_marketing boolean. The consent state field returns one of: subscribed, not_subscribed, pending, or unsubscribed. Map each state explicitly to your CRM’s subscription status field:
// Shopify marketing consent -> CRM subscription status mapping
// Shopify API 2022-04+ uses email_marketing_consent object
function mapConsentStateToCRM(shopifyCustomer) {
const consentState = shopifyCustomer.email_marketing_consent?.state
?? (shopifyCustomer.accepts_marketing ? 'subscribed' : 'not_subscribed');
// Map Shopify consent states to CRM subscription status values
// Adjust CRM_STATUS values to match your CRM's allowed field values
const CONSENT_MAP = {
'subscribed': { crmStatus: 'Subscribed', sendMarketing: true },
'not_subscribed': { crmStatus: 'Not Opted In', sendMarketing: false },
'pending': { crmStatus: 'Pending', sendMarketing: false },
'unsubscribed': { crmStatus: 'Unsubscribed', sendMarketing: false },
};
const mapped = CONSENT_MAP[consentState] ?? CONSENT_MAP['not_subscribed'];
return {
emailSubscriptionStatus: mapped.crmStatus,
allowsMarketing: mapped.sendMarketing,
consentUpdatedAt: shopifyCustomer.email_marketing_consent?.consent_updated_at,
consentSource: shopifyCustomer.email_marketing_consent?.opt_in_level,
};
}
|
Consent state changes must propagate in both directions. When a customer unsubscribes via an email campaign link managed by your CRM, the CRM must push the unsubscribe back to Shopify using the PUT /customers/{id}.json endpoint with email_marketing_consent.state: unsubscribed. Failure to sync unsubscribes from CRM back to Shopify means Shopify’s storefront may still show the customer as subscribed in account portal views.
Customer Lifetime Value and Segmentation Sync
The most commercially valuable data flowing from Shopify to a CRM is not individual order records. It is derived metrics: customer lifetime value (CLV), purchase frequency, average order value, days since last order, and product category affinity. These metrics power CRM segmentation, lead scoring, and sales rep prioritization in ways that raw order data cannot.
Computing and Syncing CLV Metrics
Compute CLV and related metrics from your Shopify order data in the integration middleware rather than attempting to compute them in the CRM using CRM formula fields. Middleware computation gives you full control over the calculation methodology and allows you to include data from multiple sources (Shopify order history, return rates, subscription revenue).
// Compute customer lifetime value metrics for CRM sync
// Run as a scheduled job: nightly or on each new order
async function computeCustomerMetrics(shop, shopifyCustomerId) {
// Query all orders for this customer from your local database
// (maintain a local order cache to avoid Shopify API rate limits)
const orders = await db.query(
`SELECT total_price, created_at, financial_status
FROM orders
WHERE shop_id = $1 AND shopify_customer_id = $2
AND financial_status = 'paid'
ORDER BY created_at DESC`,
[shop, shopifyCustomerId]
);
if (orders.rows.length === 0) return null;
const prices = orders.rows.map(o => parseFloat(o.total_price));
const totalSpend = prices.reduce((a, b) => a + b, 0);
const orderCount = orders.rows.length;
const avgOrderValue = totalSpend / orderCount;
const lastOrderDate = orders.rows[0].created_at;
const daysSinceLast = Math.floor(
(Date.now() - new Date(lastOrderDate)) / 86400000
);
// Purchase frequency: orders per 30-day period since first order
const firstOrderDate = orders.rows[orders.rows.length - 1].created_at;
const daysSinceFirst = Math.floor(
(Date.now() - new Date(firstOrderDate)) / 86400000
) || 1;
const purchaseFreq = (orderCount / daysSinceFirst) * 30;
// Churn risk: no order in 90+ days is high risk for this category
const churnRisk = daysSinceLast > 90 ? 'high'
: daysSinceLast > 45 ? 'medium' : 'low';
return {
totalSpend: Math.round(totalSpend * 100) / 100,
orderCount,
avgOrderValue: Math.round(avgOrderValue * 100) / 100,
lastOrderDate,
daysSinceLast,
purchaseFreq: Math.round(purchaseFreq * 10) / 10,
churnRisk,
clvTier: totalSpend > 1000 ? 'high'
: totalSpend > 300 ? 'medium' : 'low',
};
}
|
Sync these computed metrics as custom CRM properties on the contact record. CRM segmentation tools can then filter on clvTier, churnRisk, and daysSinceLast to build targeted re-engagement campaigns, prioritize high-value customers for sales rep outreach, and identify at-risk accounts before they lapse.
Observability for Shopify CRM Synchronization
CRM sync failures are among the hardest integration problems to detect because the symptoms appear in business outcomes rather than system errors: a sales rep working from stale contact data, a marketing campaign sent to opted-out customers, a customer service interaction without order history context. Observability must surface sync health before business stakeholders notice data quality problems.
Key CRM Sync Metrics
- Sync lag per event type: the time between a Shopify event (order created, customer updated) and the corresponding CRM record update. Alert when lag exceeds your SLA for any event type.
- Identity resolution outcomes: track the rate of email matches, phone matches, new contact creations, and ambiguous multi-match flags. A spike in ambiguous matches indicates a data quality problem requiring deduplication attention.
- Field overwrite conflicts: count how often the integration attempts to write a CRM-owned field and is blocked by the field ownership configuration. Persistent conflicts indicate a field ownership misconfiguration.
- Consent state sync errors: any failure to sync an unsubscribe event from Shopify to the CRM is a potential compliance incident. Alert on every consent sync failure without waiting for retry exhaustion.
- Dead-letter queue depth: CRM sync DLQ messages represent customer records not updated in the CRM. Each message is a data freshness gap that affects a real customer interaction.
Combine CRM sync metrics with Shopify technical mistakes observability patterns to build a complete integration health dashboard that covers both the Shopify-side event pipeline and the CRM-side record quality.
CRM Sync Architecture for Shopify Plus
Shopify Plus merchants operating with large sales teams, B2B customer segments, and high order volumes generate CRM sync requirements that differ qualitatively from standard plan merchants. The three Plus-specific architectural considerations are:
- B2B company and contact model: Shopify Plus B2B creates Company and CompanyLocation records alongside Customer records. Your CRM sync must handle the three-way relationship between Shopify Company, Shopify Customer, and CRM Account/Contact, maintaining the correct association in both systems.
- Multi-storefront customer identity: Plus merchants with multiple storefronts may have the same customer placing orders across different Shopify stores. The CRM should maintain a single contact record per customer regardless of which storefront generated the order, requiring cross-store identity resolution logic in the middleware.
- Sales rep assignment routing: Plus B2B customers are often assigned to specific sales reps in the CRM. When Shopify creates a new Company record, the middleware must route the CRM contact creation through an assignment rule that matches the company to the correct sales rep based on territory, company size, or industry.
Review Shopify vs Shopify Plus to understand which B2B API capabilities, company record structures, and rate limit tiers apply to your merchant tier before designing the CRM sync architecture for Plus deployments.
Conclusion
Shopify CRM synchronization is a customer data architecture problem. The three most critical implementation decisions are:
- Implement customer identity resolution before the first sync runs. A CRM that accumulates duplicate contacts for the same customer is harder to fix after the fact than to design around from the start. Build email-first matching with phone fallback, store the resolved CRM contact ID in your middleware database, and flag ambiguous multi-matches for manual review rather than silently picking one.
- Define field-level sync ownership and enforce it in the middleware. The integration owns commerce metrics: total spend, order count, last order date, average order value, consent state. The CRM owns relationship data: lifecycle stage, lead status, owner, notes. Write this configuration explicitly in code. A sales rep’s manual update to a lifecycle stage should survive every sync cycle without being overwritten.
- Treat consent sync as a compliance requirement, not a data quality concern. Use Shopify’s email_marketing_consent object, not the deprecated accepts_marketing boolean. Sync unsubscribes from the CRM back to Shopify on every state change. Alert on every consent sync failure individually, not on retry exhaustion. The regulatory cost of sending marketing to opted-out customers exceeds any engineering cost of over-alerting on consent failures.
Start with a field ownership matrix before writing any sync code. List every CRM field your integration touches, assign it to either integration-owned or CRM-owned, and document the conflict resolution rule for bidirectional fields. That document is the most important deliverable in any CRM integration project. Review the async Shopify architecture patterns to ensure the event pipeline delivering data to your CRM sync workers handles retries, deduplication, and dead-letter routing correctly from day one.
Frequently Asked Questions
What is Shopify CRM synchronization?
Shopify CRM synchronization is the continuous or scheduled process of propagating customer records, order events, and behavioral signals from Shopify to a CRM system, and selectively pushing CRM data such as lifecycle stage updates and contact enrichment back to Shopify. It requires explicit decisions about which system owns which data domain, how duplicate contacts are prevented through identity resolution, and how the integration recovers from partial failures without overwriting manually edited CRM fields.
How do you prevent duplicate contacts in Shopify CRM sync?
Prevent duplicate contacts by implementing customer identity resolution before creating any new CRM record. First query the CRM for an existing contact matching the Shopify customer’s email address. If no email match exists, fall back to phone number matching. If no match is found, create a new contact and store the CRM contact ID against the Shopify customer ID in your middleware database. All subsequent sync events use the stored mapping rather than re-running the resolution query.
How does Shopify Salesforce sync work?
Shopify Salesforce sync works by mapping Shopify customers to Salesforce Contacts and Shopify orders to Salesforce Orders using Salesforce’s external ID upsert pattern. Create a custom external ID field (Shopify_Customer_ID__c) on the Salesforce Contact object. Use the PATCH upsert endpoint to insert or update the Contact in a single API call based on whether the Shopify customer ID already exists in Salesforce. This eliminates the query-then-insert-or-update pattern, halves API calls, and prevents race-condition duplicate creation.
What is field-level sync ownership in CRM integration?
Field-level sync ownership is the configuration that defines which CRM fields the integration is permitted to write and which fields the CRM owns exclusively. Integration-owned fields (total spend, order count, consent state) are always overwritten by the sync. CRM-owned fields (lifecycle stage, lead status, owner, notes) are never touched by the integration regardless of what Shopify’s data contains. This prevents the integration from overwriting manual updates made by sales reps between sync cycles.
How do you sync marketing consent from Shopify to a CRM?
Use Shopify’s email_marketing_consent object (available from API version 2022-04) rather than the deprecated accepts_marketing boolean. Map the four consent states (subscribed, not_subscribed, pending, unsubscribed) to your CRM’s subscription status field values. Sync consent state changes in both directions: Shopify unsubscribes must update the CRM, and CRM unsubscribes (from email campaign opt-outs) must push back to Shopify via PUT /customers/{id}.json. Alert on every consent sync failure individually as a potential compliance incident.
