Customer Timeline Export

Read-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.

shopify-admin-customer-timeline-export


Purpose

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.


Prerequisites

  • Authenticated Shopify CLI session: shopify store auth --store --scopes read_customers,read_orders
  • API scopes: read_customers, read_orders

  • Parameters


    ParameterTypeRequiredDefaultDescription
    storestringyesStore domain (e.g., mystore.myshopify.com)
    formatstringnohumanOutput format: human or json
    customer_idstringyesGID of the customer (e.g., gid://shopify/Customer/12345)
    include_line_itemsboolnotrueInclude per-line-item rows in the CSV (one row per line item)
    include_refundsboolnotrueInclude refunds and returns as separate rows in the timeline
    max_ordersintegerno250Cap on orders fetched (most recent first); 0 = no cap

    Safety


    > ℹ️ 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.


    Workflow Steps


  • OPERATION: customer — query
  • Inputs: id: , select identity fields, lifetime aggregates, tags, note, marketing consent, addresses, createdAt

    Expected output: Customer header record; abort with a clear error if null


  • OPERATION: orders — query
  • Inputs: query: "customer_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)

    Expected output: Full chronological order list with embedded refunds and fulfillments


  • 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.

  • GraphQL Operations


    # customer:query — validated against api_version 2025-01
    query CustomerHeaderForTimeline($id: ID!) {
      customer(id: $id) {
        id
        displayName
        firstName
        lastName
        defaultEmailAddress { emailAddress }
        phone
        note
        tags
        numberOfOrders
        amountSpent { amount currencyCode }
        emailMarketingConsent { marketingState marketingOptInLevel consentUpdatedAt }
        smsMarketingConsent { marketingState marketingOptInLevel consentUpdatedAt }
        addresses(first: 25) {
          id firstName lastName address1 address2 city provinceCode countryCodeV2 zip phone
        }
        defaultAddress { id }
        createdAt
        updatedAt
      }
    }
    

    # orders:query — validated against api_version 2025-01
    query CustomerOrdersForTimeline($query: String!, $after: String) {
      orders(first: 250, after: $after, query: $query, sortKey: CREATED_AT, reverse: true) {
        edges {
          node {
            id
            name
            createdAt
            processedAt
            displayFinancialStatus
            displayFulfillmentStatus
            cancelledAt
            cancelReason
            totalPriceSet { shopMoney { amount currencyCode } }
            totalShippingPriceSet { shopMoney { amount currencyCode } }
            totalDiscountsSet { shopMoney { amount currencyCode } }
            lineItems(first: 50) {
              edges { node {
                id title quantity sku
                discountedTotalSet { shopMoney { amount currencyCode } }
                variant { id sku }
              } }
            }
            fulfillments {
              id status deliveredAt
              trackingInfo { number url company }
            }
            refunds {
              id
              createdAt
              totalRefundedSet { shopMoney { amount currencyCode } }
              refundLineItems(first: 50) {
                edges { node {
                  quantity
                  lineItem { id title }
                  subtotalSet { shopMoney { amount currencyCode } }
                } }
              }
            }
          }
        }
        pageInfo { hasNextPage endCursor }
      }
    }
    

    Session Tracking


    Claude MUST emit the following output at each stage. This is mandatory.


    On start, emit:

    ╔══════════════════════════════════════════════╗
    ║  SKILL: Customer Timeline Export             ║
    ║  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):

    ══════════════════════════════════════════════
    CUSTOMER TIMELINE
      Customer:        <name> (<email>)  ID: <customer_id>
      Joined:          <createdAt>
      Lifetime spend:  $<amount> <currency>   Total orders: <n>   Refunds total: $<amount>
      Tags:            <list>
      Email consent:   <state> (since <date>)   SMS consent: <state> (since <date>)
    
      Recent timeline (most recent first):
        <date>  ORDER   <name>  $<amount>  <financial> / <fulfillment>
        <date>  REFUND  <id>    $<amount>
        <date>  RETURN  <id>    items: <n>
        ...
      Output: customer_timeline_<customer_id>_<date>.csv
    ══════════════════════════════════════════════
    

    For format: json, emit:

    {
      "skill": "customer-timeline-export",
      "store": "<domain>",
      "customer_id": "<gid>",
      "header": {
        "name": "<string>", "email": "<string>", "phone": "<string>",
        "joined": "<ISO8601>", "lifetime_spend": 0, "currency": "USD",
        "total_orders": 0, "tags": [],
        "email_consent": "<state>", "sms_consent": "<state>"
      },
      "totals": { "orders": 0, "refunds_count": 0, "refunds_amount": 0 },
      "output_file": "customer_timeline_<customer_id>_<date>.csv"
    }
    

    Output Format

    CSV file customer_timeline__.csv with columns:

    event_datetime, event_type, event_id, order_name, line_item_title, sku, quantity, amount, currency, financial_status, fulfillment_status, tracking_number, notes


    Event types include: customer_created, order_placed, order_fulfilled, order_delivered, order_cancelled, refund_issued, return_initiated.


    Error Handling

    ErrorCauseRecovery
    THROTTLEDAPI rate limit exceededWait 2 seconds, retry up to 3 times
    Customer not foundWrong GIDUse order-lookup-and-summary to find a recent order, then read customer.id
    0 ordersNew account or guest-onlyEmit header-only timeline
    Order anonymizedGDPR data wipeSkip line items; emit placeholder row
    Pagination beyond max_ordersCap reachedStop, set truncated: true

    Best Practices

  • Run before any merge, deletion, or escalation — pre-state is unreconstructable once a merge commits.
  • For high-volume customers set max_orders: 50; full lifetime is rarely needed for one support case.
  • CSV is event-sorted so downstream pivoting works without reshaping.
  • Pair with customer-merge: run on both winner and loser before merging for a permanent pre-merge record.