$npx -y skills add 40RTY-ai/shopify-admin-skills --skill shopify-admin-customer-timeline-exportRead-only: exports a complete chronological history for a single customer — orders, refunds, returns, addresses, notes, tags, marketing consent, and lifetime spend — as one consolidated CSV.
| 1 | ## Purpose |
| 2 | Produces a complete, chronological dossier for a single customer. Pulls the customer record (identity, marketing consent, lifetime totals, tags, notes, addresses) and every order they've placed (with line items, fulfillments, refunds, and returns) and emits one merged CSV plus a human-readable timeline. Used by support agents handling escalations, by data export requests, and as the source-of-truth dump before an account merge or deletion. Read-only — no mutations. |
| 3 | |
| 4 | ## Prerequisites |
| 5 | - Authenticated Shopify CLI session: `shopify store auth --store <domain> --scopes read_customers,read_orders` |
| 6 | - API scopes: `read_customers`, `read_orders` |
| 7 | |
| 8 | ## Parameters |
| 9 | |
| 10 | | Parameter | Type | Required | Default | Description | |
| 11 | |-----------|------|----------|---------|-------------| |
| 12 | | store | string | yes | — | Store domain (e.g., mystore.myshopify.com) | |
| 13 | | format | string | no | human | Output format: `human` or `json` | |
| 14 | | customer_id | string | yes | — | GID of the customer (e.g., `gid://shopify/Customer/12345`) | |
| 15 | | include_line_items | bool | no | true | Include per-line-item rows in the CSV (one row per line item) | |
| 16 | | include_refunds | bool | no | true | Include refunds and returns as separate rows in the timeline | |
| 17 | | max_orders | integer | no | 250 | Cap on orders fetched (most recent first); 0 = no cap | |
| 18 | |
| 19 | ## Safety |
| 20 | |
| 21 | > ℹ️ Read-only skill — no mutations are executed. Safe to run at any time. Output contains personally identifiable information — handle the resulting CSV with the same care as any customer export and delete it once the support case is closed. |
| 22 | |
| 23 | ## Workflow Steps |
| 24 | |
| 25 | 1. **OPERATION:** `customer` — query |
| 26 | **Inputs:** `id: <customer_id>`, select identity fields, lifetime aggregates, tags, note, marketing consent, addresses, createdAt |
| 27 | **Expected output:** Customer header record; abort with a clear error if `null` |
| 28 | |
| 29 | 2. **OPERATION:** `orders` — query |
| 30 | **Inputs:** `query: "customer_id:<numeric_id>"`, `first: 250`, `sortKey: CREATED_AT`, `reverse: true`, select `id`, `name`, `createdAt`, `processedAt`, `displayFinancialStatus`, `displayFulfillmentStatus`, `totalPriceSet`, `totalShippingPriceSet`, `totalDiscountsSet`, `lineItems(first: 50) { node { id title quantity sku discountedTotalSet variant { sku } } }`, `fulfillments { id status deliveredAt trackingInfo { number url } }`, `refunds { id createdAt totalRefundedSet refundLineItems(first: 50) { node { lineItem { id title } quantity subtotalSet } } }`, pagination cursor (stop at `max_orders` if set) |
| 31 | **Expected output:** Full chronological order list with embedded refunds and fulfillments |
| 32 | |
| 33 | 3. Build the merged timeline events: customer creation → each order → each refund/return per order. Sort all events by datetime ascending for the human-readable summary; the CSV is also sorted ascending. |
| 34 | |
| 35 | ## GraphQL Operations |
| 36 | |
| 37 | ```graphql |
| 38 | # customer:query — validated against api_version 2025-01 |
| 39 | query CustomerHeaderForTimeline($id: ID!) { |
| 40 | customer(id: $id) { |
| 41 | id |
| 42 | displayName |
| 43 | firstName |
| 44 | lastName |
| 45 | defaultEmailAddress { emailAddress } |
| 46 | phone |
| 47 | note |
| 48 | tags |
| 49 | numberOfOrders |
| 50 | amountSpent { amount currencyCode } |
| 51 | emailMarketingConsent { marketingState marketingOptInLevel consentUpdatedAt } |
| 52 | smsMarketingConsent { marketingState marketingOptInLevel consentUpdatedAt } |
| 53 | addresses(first: 25) { |
| 54 | id firstName lastName address1 address2 city provinceCode countryCodeV2 zip phone |
| 55 | } |
| 56 | defaultAddress { id } |
| 57 | createdAt |
| 58 | updatedAt |
| 59 | } |
| 60 | } |
| 61 | ``` |
| 62 | |
| 63 | ```graphql |
| 64 | # orders:query — validated against api_version 2025-01 |
| 65 | query CustomerOrdersForTimeline($query: String!, $after: String) { |
| 66 | orders(first: 250, after: $after, query: $query, sortKey: CREATED_AT, reverse: true) { |
| 67 | edges { |
| 68 | node { |
| 69 | id |
| 70 | name |
| 71 | createdAt |
| 72 | processedAt |
| 73 | displayFinancialStatus |
| 74 | displayFulfillmentStatus |
| 75 | cancelledAt |
| 76 | cancelReason |
| 77 | totalPriceSet { shopMoney { amount currencyCode } } |
| 78 | totalShippingPriceSet { shopMoney { amount currencyCode } } |
| 79 | totalDiscountsSet { shopMoney { amount currencyCode } } |
| 80 | lineItems(first: 50) { |
| 81 | edges { node { |
| 82 | id title quantity sku |
| 83 | discountedTotalSet { shopMoney { amount currencyCode } } |
| 84 | variant { id sku } |
| 85 | } } |
| 86 | } |
| 87 | fulfillments { |
| 88 | id status deliveredAt |
| 89 | trackingInfo { number url company } |
| 90 | } |
| 91 | refunds { |
| 92 | id |
| 93 | createdAt |
| 94 | totalRefundedSet { shopMoney { amount currencyCod |