From shopify-admin-skills
Parses UTM parameters from Shopify order landing site URLs to attribute revenue, AOV, and conversion volume to marketing channels like source/medium/campaign.
npx claudepluginhub 40rty-ai/shopify-admin-skills --plugin shopify-admin-skillsThis skill uses the workspace's default tool permissions.
Pulls recent orders, extracts the UTM parameters embedded in each order's `landingPageUrl` query string, and rolls up revenue, order count, and average order value (AOV) by `utm_source`, `utm_medium`, and `utm_campaign`. Builds a marketing attribution report directly from first-party Shopify order data — no external analytics tool required. Read-only — no mutations.
Parses Shopify orders' landing/referrer URLs and UTMs to attribute orders, revenue, AOV by traffic sources: direct, organic, paid, social, email, referrals. For granular marketing analytics beyond native dashboards.
Queries Wix orders via REST API with filters for date ranges, payment/fulfillment status, value; supports revenue, trend, cohort analysis.
Analyzes D2C ecommerce order CSV data across 30d/90d/365d periods to generate KPI trees, health signals, structured findings, and action plans.
Share bugs, ideas, or general feedback.
Pulls recent orders, extracts the UTM parameters embedded in each order's landingPageUrl query string, and rolls up revenue, order count, and average order value (AOV) by utm_source, utm_medium, and utm_campaign. Builds a marketing attribution report directly from first-party Shopify order data — no external analytics tool required. Read-only — no mutations.
shopify store auth --store <domain> --scopes read_ordersread_orders| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| store | string | yes | — | Store domain (e.g., mystore.myshopify.com) |
| days_back | integer | no | 30 | Lookback window for orders to attribute |
| group_by | string | no | source | Primary grouping dimension: source, medium, campaign, or source_medium |
| min_orders | integer | no | 1 | Minimum orders per group to include in the report |
| include_organic | bool | no | true | When false, omit orders with no UTM parameters from the breakdown |
| format | string | no | human | Output format: human or json |
ℹ️ Read-only skill — no mutations are executed. Safe to run at any time. Attribution accuracy depends on whether the storefront propagates UTM parameters into the checkout — orders that bypass the storefront (POS, draft orders, subscriptions) will not have landing site URLs.
OPERATION: orders — query
Inputs: query: "created_at:>='<NOW - days_back days>' financial_status:paid", first: 250, select landingPageUrl, referrerUrl, customerJourneySummary, totalPriceSet, pagination cursor
Expected output: All paid orders in the window with landing page URLs; paginate until hasNextPage: false
For each order, parse landingPageUrl query string and extract utm_source, utm_medium, utm_campaign, utm_term, utm_content. Orders without UTM params are bucketed as (direct/organic) if include_organic: true.
Aggregate by the group_by dimension: sum order count, sum revenue (in shop currency), compute AOV = revenue / orders.
Sort groups by revenue descending; filter out groups below min_orders.
# orders:query — validated against api_version 2025-01
query OrdersWithAttribution($query: String!, $after: String) {
orders(first: 250, after: $after, query: $query) {
edges {
node {
id
name
createdAt
landingPageUrl
referrerUrl
displayFinancialStatus
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
customerJourneySummary {
firstVisit {
landingPage
source
sourceType
referrerUrl
utmParameters {
source
medium
campaign
term
content
}
}
lastVisit {
landingPage
source
sourceType
utmParameters {
source
medium
campaign
}
}
momentsCount
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
Claude MUST emit the following output at each stage. This is mandatory.
On start, emit:
╔══════════════════════════════════════════════╗
║ SKILL: Order Attribution Report ║
║ 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>
On completion, emit:
For format: human (default):
══════════════════════════════════════════════
ORDER ATTRIBUTION REPORT (<days_back> days)
Orders attributed: <n>
Total revenue: $<amount>
Untagged (direct): <n> (<pct>%)
Top sources by revenue:
<source> Orders: <n> Revenue: $<n> AOV: $<n>
<source> Orders: <n> Revenue: $<n> AOV: $<n>
Output: attribution_report_<date>.csv
══════════════════════════════════════════════
For format: json, emit:
{
"skill": "order-attribution-report",
"store": "<domain>",
"period_days": 30,
"group_by": "source",
"orders_attributed": 0,
"total_revenue": 0,
"currency": "USD",
"groups": [
{ "key": "google", "orders": 0, "revenue": 0, "aov": 0 }
],
"output_file": "attribution_report_<date>.csv"
}
CSV file attribution_report_<YYYY-MM-DD>.csv with columns:
group_key, utm_source, utm_medium, utm_campaign, orders, revenue, aov, currency, pct_of_revenue
| Error | Cause | Recovery |
|---|---|---|
THROTTLED | API rate limit exceeded | Wait 2 seconds, retry up to 3 times |
landingPageUrl is null | Order placed via POS, draft, or subscription | Bucket as (direct/organic), count separately |
| Malformed query string | Manual or partial UTM tagging | Skip parse failure, treat as direct, log count |
customerJourneySummary access denied | Store on plan that does not expose this field | Fall back to landingPageUrl parsing only |
group_by: source_medium to distinguish paid traffic (google/cpc) from organic (google/organic).(direct/organic) percentage usually means UTM tagging is missing on paid campaigns — fix the campaign URLs, not the report.customerJourneySummary.firstVisit vs lastVisit to compare first-click vs last-click models.