---
name: shopify-ventas-por-fuente-de-trafico
description: "Solo lectura: analiza la página de llegada y el sitio de referencia de cada pedido para mostrar pedidos, ingresos y ticket promedio por fuente: directo, orgánico, pago, redes sociales, correo o referidos. Úsala cuando quieras saber qué canales te traen ventas."
---

## Propósito
Agrupa los pedidos según la fuente de tráfico del primer contacto, extraída del `landingPageUrl` y el `referrerUrl` de cada pedido y de los parámetros UTM que traiga la URL de llegada. Genera una tabla de atribución con pedidos, ingresos y ticket promedio (AOV) por fuente, para que veas qué canales realmente convierten. Solo lectura: no ejecuta mutaciones. Úsala cuando los paneles de análisis nativos de Shopify no tengan el detalle que necesitas o cuando quieras exportar los datos de atribución sin procesar para un modelo externo.

## Requisitos
- Sesión autenticada de Shopify CLI: `shopify store auth --store <tu-tienda>.myshopify.com --scopes read_orders`
- Las consultas se ejecutan con `shopify store execute`.
- Permisos (scopes) de la API: `read_orders`

## Parámetros

| Parámetro | Tipo | Obligatorio | Por defecto | Descripción |
|-----------|------|-------------|-------------|-------------|
| store | string | sí | — | Dominio de la tienda (p. ej., tu-tienda.myshopify.com) |
| format | string | no | human | Formato de salida: `human` o `json` |
| days_back | integer | no | 30 | Cuántos días hacia atrás analizar |
| min_orders | integer | no | 1 | Pedidos mínimos por fuente para que aparezca en el resumen legible |
| group_by | string | no | category | Nivel de agrupación: `category` (direct/organic/paid/social/email/referral), `domain` (dominio de referencia sin procesar) o `utm_source` (valor del parámetro UTM) |
| include_utm | bool | no | true | Si es true, extrae `utm_source`, `utm_medium` y `utm_campaign` de los parámetros de `landingPageUrl` |

## Seguridad

> ℹ️ Skill de solo lectura: no ejecuta mutaciones. Puedes correrla en cualquier momento.

## Pasos

1. **OPERACIÓN:** `orders` — consulta
   **Entradas:** `query: "created_at:>='<NOW - days_back days>'"`, `first: 250`, selecciona `id`, `name`, `createdAt`, `landingPageUrl`, `referrerUrl`, `customerJourneySummary { firstVisit { landingPage referrerUrl source sourceType utmParameters { source medium campaign term content } } }`, `totalPriceSet`, `customer { numberOfOrders }`, cursor de paginación
   **Resultado esperado:** Pedidos con sus datos de llegada, referencia y UTM; pagina hasta que `hasNextPage: false`

2. Para cada pedido, determina una fuente normalizada:
   - Si `customerJourneySummary.firstVisit.utmParameters.source` tiene valor → úsalo (es el dato más confiable)
   - Si no, extrae los parámetros UTM de `landingPageUrl` cuando `include_utm: true`
   - Si no, toma el dominio de `referrerUrl` y asígnale una categoría:
     - vacío/null → `direct`
     - google.com / bing.com / duckduckgo.com → `organic-search`
     - googleads/doubleclick → `paid-search`
     - facebook.com / instagram.com / tiktok.com / x.com / twitter.com / pinterest.com / youtube.com → `social-<host>`
     - dominios de correo (mail/gmail/outlook) → `email`
     - cualquier otro dominio → `referral-<host>`

3. Agrupa según la dimensión elegida en `group_by`:
   - cantidad de pedidos
   - ingresos = Σ `totalPriceSet.shopMoney.amount`
   - ticket promedio = ingresos / pedidos
   - % de clientes nuevos (pedidos con `customer.numberOfOrders == 1` dividido entre el total de la fuente)

## Operaciones GraphQL

```graphql
# orders:query — validado con api_version 2025-01
query OrdersForAttribution($query: String!, $after: String) {
  orders(first: 250, after: $after, query: $query) {
    edges {
      node {
        id
        name
        createdAt
        landingPageUrl
        referrerUrl
        totalPriceSet {
          shopMoney { amount currencyCode }
        }
        customer {
          id
          numberOfOrders
        }
        customerJourneySummary {
          firstVisit {
            landingPage
            referrerUrl
            source
            sourceType
            utmParameters {
              source
              medium
              campaign
              term
              content
            }
          }
          momentsCount {
            count
          }
        }
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

## Seguimiento de la sesión

**Claude DEBE mostrar esta salida en cada etapa. Es obligatorio.**

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: Ventas por fuente de tráfico         ║
║  Tienda: <dominio de la tienda>              ║
║  Inicio: <YYYY-MM-DD HH:MM UTC>              ║
╚══════════════════════════════════════════════╝
```

**Después de cada paso**, muestra:
```
[N/TOTAL] <CONSULTA|MUTACIÓN>  <NombreOperación>
          → Parámetros: <resumen breve de las entradas clave>
          → Resultado: <conteo o resultado>
```

**Al terminar**, muestra:

Para `format: human` (por defecto):
```
══════════════════════════════════════════════
REPORTE DE ATRIBUCIÓN  (<days_back> días, agrupado por: <group_by>)
  Pedidos analizados:    <n>
  Ingresos totales:      $<monto>
  Fuentes detectadas:    <n>

  Principales fuentes por ingresos
  ─────────────────────────────────────────
  <fuente>            Pedidos: <n>  Ingresos: $<n>  Ticket prom.: $<n>  Clientes nuevos: <pct>%
  <fuente>            Pedidos: <n>  Ingresos: $<n>  Ticket prom.: $<n>  Clientes nuevos: <pct>%
  ...

  Archivo: attribution_<fecha>.csv
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-ventas-por-fuente-de-trafico",
  "store": "<dominio>",
  "period_days": 30,
  "group_by": "category",
  "totals": {
    "orders": 0,
    "revenue": 0,
    "currency": "<moneda de la tienda>"
  },
  "sources": [
    {
      "source": "<nombre>",
      "orders": 0,
      "revenue": 0,
      "aov": 0,
      "new_customer_pct": 0
    }
  ],
  "output_file": "attribution_<fecha>.csv"
}
```

## Formato de salida
Archivo CSV `attribution_<YYYY-MM-DD>.csv` con las columnas:
`order_id`, `order_name`, `created_at`, `source`, `source_category`, `referrer_url`, `landing_page_url`, `utm_source`, `utm_medium`, `utm_campaign`, `revenue`, `is_new_customer`

## Manejo de errores
| Error | Causa | Solución |
|-------|-------|----------|
| `THROTTLED` | Se superó el límite de solicitudes de la API | Espera 2 segundos y reintenta hasta 3 veces |
| `landingPageUrl` y `referrerUrl` en null | Pedido de POS, borrador o importado | Clasifícalo como `unattributed` |
| Parámetros UTM mal formados | Caracteres sin codificar en la URL de llegada | Omite la lectura de UTM y usa el dominio de referencia |
| `customerJourneySummary` no disponible | Pedido antiguo o creado por una app | Usa los campos generales `landingPageUrl`/`referrerUrl` |

## Buenas prácticas
- Usa `group_by: utm_source` cuando manejes campañas estructuradas con un etiquetado UTM consistente: es el dato de atribución más fiel.
- Usa `group_by: category` para resúmenes gerenciales; los dueños de tienda quieren saber «cuánto vino de redes sociales» antes que «cuánto vino de `instagram.com/p/abc`».
- Cruza los resultados con `shopify-roi-descuentos`: combinar «qué fuente trajo el pedido» con «qué descuento usó el pedido» muestra dónde la adquisición pagada realmente rinde.
- Ojo con el tráfico «directo» inflado: muchos clics desde apps de correo y de redes sociales pierden el sitio de referencia y aparecen como directos. Usa etiquetas UTM en tus enlaces salientes para recuperar ese dato.
- En tiendas con poco volumen, analiza varios meses (`days_back: 90`) para que los porcentajes no dependan de un puñado de pedidos.
