From shopify-admin-skills
Identifies top N% Shopify customers by lifetime spend or order frequency, exports VIP candidate CSV, and optionally tags qualified customers via GraphQL mutations.
npx claudepluginhub 40rty-ai/shopify-admin-skills --plugin shopify-admin-skillsThis skill uses the workspace's default tool permissions.
Ranks customers by lifetime spend and order frequency, identifies the top N% (by value, frequency, or both), and outputs a CSV of VIP candidates. Optionally applies a VIP tag to qualified customers via `customerUpdate`. Used to build loyalty segments, prioritize white-glove support, or seed exclusive-access campaigns. The lifetime spend and order count are pulled directly from Shopify customer ...
Identifies high-LTV Shopify customers by min order count and lifetime spend, tags qualifying ones, and exports loyalty-ready contact list.
Manages Shopify customers via GraphQL Admin API mutations/queries for create/update/delete/tags/metafields, Customer Account API, Multipass SSO, segmentation, B2B accounts.
Manages Shopify orders, customers, and fulfillments via GraphQL Admin API. Query orders, process fulfillments, handle customer records, create draft orders.
Share bugs, ideas, or general feedback.
Ranks customers by lifetime spend and order frequency, identifies the top N% (by value, frequency, or both), and outputs a CSV of VIP candidates. Optionally applies a VIP tag to qualified customers via customerUpdate. Used to build loyalty segments, prioritize white-glove support, or seed exclusive-access campaigns. The lifetime spend and order count are pulled directly from Shopify customer aggregates — no external CRM required.
shopify store auth --store <domain> --scopes read_customers,read_orders,write_customersread_customers, read_orders, write_customers (only if tag_customers: true)| 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 VIP list without applying tags |
| rank_by | string | no | spend | Ranking strategy: spend (lifetime value), frequency (order count), or both (composite score) |
| top_pct | float | no | 5 | Top percentile to qualify as VIP (e.g., 5 = top 5%) |
| min_orders | integer | no | 2 | Minimum lifetime orders to be eligible |
| min_spend | float | no | 0 | Minimum lifetime spend (shop currency) to be eligible |
| tag_customers | bool | no | false | If true, apply VIP tag to qualified customers via customerUpdate |
| tag | string | no | vip | Tag string applied when tag_customers: true |
⚠️ When
tag_customers: true, Step 3 executescustomerUpdatemutations that mutate customer tag lists. Tags persist until manually removed. Run withdry_run: truefirst to confirm the VIP list and qualifying thresholds. The default isdry_run: true— you must explicitly setdry_run: falseandtag_customers: trueto apply tags.
OPERATION: customers — query
Inputs: first: 250, query: "orders_count:>=<min_orders>", select id, displayName, defaultEmailAddress { emailAddress }, numberOfOrders, amountSpent { amount currencyCode }, tags, pagination cursor
Expected output: All customers meeting min_orders threshold; paginate until hasNextPage: false
OPERATION: orders — query (only when ranking by frequency, for recency annotation)
Inputs: For each top candidate: query: "customer_id:<id>", first: 1, sortKey: CREATED_AT, reverse: true
Expected output: Most recent order per candidate to annotate the export
Filter to amountSpent.amount >= min_spend. Score each customer: spend → spend; frequency → orders; both → 0.6 × normalized spend + 0.4 × normalized frequency. Take the top top_pct%.
OPERATION: customerUpdate — mutation (only if tag_customers: true and dry_run: false)
Inputs: input: { id: <customer_id>, tags: [...existing_tags, <tag>] }
Expected output: customer.id, customer.tags, userErrors
# customers:query — validated against api_version 2025-01
query VIPCandidateCustomers($first: Int!, $after: String, $query: String) {
customers(first: $first, after: $after, query: $query) {
edges {
node {
id
displayName
firstName
lastName
defaultEmailAddress {
emailAddress
}
numberOfOrders
amountSpent {
amount
currencyCode
}
tags
createdAt
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
# orders:query — validated against api_version 2025-01
query VIPLastOrder($query: String!) {
orders(first: 1, query: $query, sortKey: CREATED_AT, reverse: true) {
edges {
node {
id
name
createdAt
totalPriceSet {
shopMoney { amount currencyCode }
}
}
}
}
}
# customerUpdate:mutation — validated against api_version 2025-01
mutation CustomerUpdateVipTag($input: CustomerInput!) {
customerUpdate(input: $input) {
customer {
id
displayName
tags
}
userErrors {
field
message
}
}
}
Claude MUST emit the following output at each stage. This is mandatory.
On start, emit:
╔══════════════════════════════════════════════╗
║ SKILL: VIP Customer Identifier ║
║ Store: <store domain> ║
║ Started: <YYYY-MM-DD HH:MM UTC> ║
╚══════════════════════════════════════════════╝
After each step, emit:
[N/TOTAL] <QUERY|MUTATION> <OperationName>
→ Params: <brief summary of key inputs>
→ Result: <count or outcome>
If dry_run: true, prefix every mutation step with [DRY RUN] and do not execute it.
On completion, emit:
For format: human (default):
══════════════════════════════════════════════
VIP CUSTOMER REPORT
Customers scanned: <n>
Eligible (≥ min): <n>
VIPs (top <pct>%): <n>
Threshold spend: $<amount>
Threshold orders: <n>
Customers tagged: <n> (or "skipped — dry_run")
Top 10 VIPs by <rank_by>:
<name> Spend: $<amount> Orders: <n> Last: <date>
Output: vip_customers_<date>.csv
══════════════════════════════════════════════
For format: json, emit:
{
"skill": "vip-customer-identifier",
"store": "<domain>",
"dry_run": true,
"rank_by": "spend",
"outcome": {
"customers_scanned": 0,
"eligible": 0,
"vips_identified": 0,
"threshold_spend": 0,
"customers_tagged": 0,
"errors": 0,
"output_file": "vip_customers_<date>.csv"
}
}
CSV file vip_customers_<YYYY-MM-DD>.csv with columns:
customer_id, name, email, lifetime_spend, currency, orders_count, last_order_date, composite_score, rank, tag_applied
| Error | Cause | Recovery |
|---|---|---|
THROTTLED | API rate limit exceeded | Wait 2 seconds, retry up to 3 times |
userErrors on customerUpdate | Customer not found or tag conflict | Log, skip, continue |
Fewer eligible than top_pct% | Small customer base | Lower min_orders/min_spend |
| Multi-currency stores | currencyCode varies | Convert via shop default before ranking |
rank_by: both to balance whales with loyalists — pure spend ranking can over-index on one-time large purchases.vip-2026-Q2) so lapsed VIPs roll off rather than accumulating permanently.customer-win-back — VIPs who become inactive should be flagged for high-priority re-engagement.dry_run: true first; review the threshold spend value to confirm the cutoff matches your VIP definition.