Skip to content
Operations
Skill

/shopify-admin-customer-merge

Merges duplicate customer records: invokes Shopify's native customer merge API where supported, otherwise consolidates the loser record's tags and notes into the winner via customerUpdate.

From plugin
shopify-admin-skills
175116 skills
Install
$ npx -y skills add 40rty-ai/shopify-admin-skills --skill shopify-admin-customer-merge --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/shopify-admin-customer-merge

Context preview

The summary Claude sees to decide when to auto-load this skill.

Merges duplicate customer records: invokes Shopify's native customer merge API where supported, otherwise consolidates the loser record's tags and notes into the winner via customerUpdate.

SKILL.md

shopify-admin-customer-merge.SKILL.md
name: shopify-admin-customer-merge
role: customer-support
description: "Merges duplicate customer records: invokes Shopify's native customer merge API where supported, otherwise consolidates the loser record's tags and notes into the winner via customerUpdate."
toolkit: shopify-admin, shopify-admin-execution
api_version: "2025-01"
graphql_operations:
  - customer:query
  - customerMerge:mutation
  - customerUpdate:mutation
status: stable
compatibility: Claude Code, Cursor, Codex, Gemini CLI

Purpose

Resolves duplicate customer records identified by `duplicate-customer-finder`. Where the Shopify Admin API exposes `customerMerge` (a native merge that moves orders, addresses, subscriptions, and metafields onto a winner record), this skill calls it directly. When `customerMerge` is unavailable or fails for the given account pair, the skill falls back to consolidating searchable metadata — tags, notes, marketing consent — onto the winner via `customerUpdate`, then writes a clear annotation to the loser record so staff can complete the merge manually in Shopify Admin.

Prerequisites

  • Authenticated Shopify CLI session: `shopify store auth --store <domain> --scopes read_customers,write_customers`
  • API scopes: `read_customers`, `write_customers`

Parameters

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | store | string | yes | — | Store domain (e.g., mystore.myshopify.com) | | format | string | no | human | Output format: `human` or `json` | | dry_run | bool | no | true | Preview merge plan without executing mutations | | customer_winner_id | string | yes | — | GID of the customer record to keep (e.g., `gid://shopify/Customer/12345`) | | customer_loser_id | string | yes | — | GID of the customer record to merge into the winner | | use_native_merge | bool | no | true | Try `customerMerge` first; if it fails or is unavailable, fall back to consolidation via `customerUpdate` | | merge_tags | bool | no | true | Union the loser's tags onto the winner | | merge_note | bool | no | true | Append the loser's note to the winner (with timestamp prefix) | | annotate_loser | bool | no | true | Write a note on the loser record pointing to the winner GID for manual cleanup |

Safety

> ⚠️ Steps 2–4 execute mutations that modify customer records. `customerMerge` is irreversible — once orders and addresses are moved to the winner, the loser record is closed and cannot be split back. Run with `dry_run: true` first to confirm winner/loser GIDs and the merge plan. The default is `dry_run: true`. Always verify both records belong to the same human (matching email, phone, name) using `duplicate-customer-finder` output before committing. Do not merge a customer with active subscriptions or unfulfilled orders without confirming downstream systems will follow the new owner GID.

Workflow Steps

1. **OPERATION:** `customer` — query (called twice: winner and loser) **Inputs:** `id: <customer_id>`, select `id`, `displayName`, `firstName`, `lastName`, `defaultEmailAddress { emailAddress }`, `phone`, `tags`, `note`, `numberOfOrders`, `amountSpent`, `emailMarketingConsent { marketingState }`, `smsMarketingConsent { marketingState }`, `addresses(first: 25) { id }`, `createdAt` **Expected output:** Both records' full identity payload — abort if either GID does not resolve

2. **OPERATION:** `customerMerge` — mutation (only if `use_native_merge: true` and not `dry_run`) **Inputs:** `customerOneId: <customer_winner_id>`, `customerTwoId: <customer_loser_id>`, `overrideFields`: prefer winner's name/email/phone/locale/marketing-consent **Expected output:** `job.id` (merge runs asynchronously), `userErrors`. If `userErrors` indicates merge is not supported for this pair (B2B, gift card holder, subscriber, etc.), proceed to step 3 fallback.

3. **OPERATION:** `customerUpdate` — mutation (winner) — fallback path or when `use_native_merge: false` **Inputs:** `input.id: <customer_winner_id>`, `input.tags: <union of winner.tags and loser.tags>` (only if `merge_tags`), `input.note: <winner.note + "\n[YYYY-MM-DD] Merged from <loser_email>:\n" + loser.note>` (only if `merge_note`) **Expected output:** `customer.id`, `customer.tags`, `customer.note`, `userErrors`

4. **OPERATION:** `customerUpdate` — mutation (loser) — only if `annotate_loser: true` **Inputs:** `input.id: <customer_loser_id>`, `input.note: "<existing note>\n[YYYY-MM-DD] DUPLICATE — merge target: <customer_winner_id>. Manually close in Shopify Admin once orders are reviewed."`, `input.tags: <existing + ["duplicate", "merged-loser"]>` **Expected output:** `customer.id`, `customer.tags`, `customer.note`, `userErrors`

GraphQL Operations

# customer:query — validated against api_version 2025-01
query CustomerForMerge($id: ID!) {
  customer(id: $id) {
    id
    displayName
    firstName
    lastName
    defaultEmailAddress { emailAddress }
    phone
    tags
    note
    numberOfOrders
    amountSpent { amount currencyCode }
    emailMarketingConsent { marketingState marketingOptInLevel consentUpdatedAt }
    smsMarketingConsent { marketingState marketingOptInLevel consentUpdatedAt }
    addresses(first: 25) { id address1 city provinceCode countryCodeV2 zip }
    createdAt
  }
}
# customerMerge:mutation — validated against api_version 2025-01
mutation CustomerMerge(
  $customerOneId: ID!
  $customerTwoId: ID!
  $overrideFields: CustomerMergeOverrideFields
) {
  customerMerge(
    customerOneId: $customerOneId
    customerTwoId: $customerTwoId
    overrideFields: $overrideFields
  ) {
    job { id done }
    resultingCustomerId
    userErrors { field message code }
  }
}
# customerUpdate:mutation — validated against api_version 2025-01
mutation CustomerConsolidate($input: CustomerInput!) {
  customerUpdate(input: $input) {
    customer { id displayName tags note }
    userErrors { field message }
  }
}

Session Trac

Read more
Ships withshopify-admin-skills

Community-maintained AI agent skills for operating Shopify stores — workflows, optimization, reports and more

Get the whole plugin