From shopify-admin-skills
Parses UTM parameters from Shopify order landing page URLs and builds a revenue, AOV, and conversion attribution report grouped by source, medium, or campaign.
How this skill is triggered — by the user, by Claude, or both
Slash command
/shopify-admin-skills:shopify-admin-order-attribution-reportThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
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.npx claudepluginhub 40rty-ai/shopify-admin-skills --plugin shopify-admin-skillsRead-only: parses each order's landing site and referrer URL to break down orders, revenue, and AOV by traffic source — direct, organic, paid, social, email, or referral domain.
Queries Wix orders via REST API for revenue calculation, trend analysis, and cohort analysis. Filters by date range, payment/fulfillment status, and value.
Runs multi-touch attribution analysis comparing first-touch, last-touch, linear, time-decay, position-based, and data-driven models for marketing revenue allocation.