shopify-admin-vip-customer-identifier
SkillCommerce & financeIdentifies top-spending customers (top N% by lifetime value or order frequency) and exports a VIP candidate list; optionally tags qualified customers as VIPs.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the shopify-admin-vip-customer-identifier skill
What this skill tells your AI
The instructions your AI receives, as published by 40rty-ai/shopify-admin-skills in skills/marketing/shopify-admin-vip-customer-identifier/SKILL.md and read by ahel’s review.
Purpose
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.
Prerequisites
- Authenticated Shopify CLI session:
shopify store auth --store <domain> --scopes read_customers,read_orders,write_customers - API scopes:
read_customers,read_orders,write_customers(only iftag_customers: true)
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 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 |
Safety
⚠️ 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.
Workflow Steps
-
OPERATION:
customers— query Inputs:first: 250,query: "orders_count:>=<min_orders>", selectid,displayName,defaultEmailAddress { emailAddress },numberOfOrders,amountSpent { amount currencyCode },tags, pagination cursor Expected output: All customers meetingmin_ordersthreshold; paginate untilhasNextPage: 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: trueExpected 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 toptop_pct%. -
OPERATION:
customerUpdate— mutation (only iftag_customers: trueanddry_run: false) Inputs:input: { id: <customer_id>, tags: [...existing_tags, <tag>] }Expected output:customer.id,customer.tags,userErrors
GraphQL Operations
# 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
}
}
}
Session Tracking
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"
}
}
Output Format
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 Handling
| 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 |
Best Practices
- Use
rank_by: bothto balance whales with loyalists — pure spend ranking can over-index on one-time large purchases. - Re-run quarterly with a date-stamped tag (e.g.,
vip-2026-Q2) so lapsed VIPs roll off rather than accumulating permanently. - Pair with
customer-win-back— VIPs who become inactive should be flagged for high-priority re-engagement. - Run with
dry_run: truefirst; review the threshold spend value to confirm the cutoff matches your VIP definition.
Signals
- GitHub stars
- 187
- Forks
- 18
- Last commit
- Aug 2026
ahel review
S4info
community integration — published by 40rty-ai, not shopify
Automated review, not a security audit. Ruleset v1.
Advanced
- Catalog kind
- skill
- Gateway key
shopify-admin-vip-customer-identifier- Source
- github.com/40rty-ai/shopify-admin-skills