---
name: shopify-notas-clientes-masivas
description: "Agrega notas internas a muchas fichas de clientes a la vez: marcas después de una campaña, anotaciones de importación o contexto para atención al cliente. Úsala cuando necesites dejar la misma nota en un grupo de clientes."
---

## Propósito
Consulta los clientes que cumplen un filtro (etiqueta, lista de correos o un mínimo de gasto) y agrega una nota a la ficha de cada uno. Las notas internas las ve el equipo en el panel de Shopify, pero no los clientes. Sirve para anotar quién participó en una campaña, registrar de dónde vino una importación, marcar clientes VIP o dejar contexto para atención al cliente.

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

## Parámetros

| Parámetro | Tipo | Obligatorio | Predeterminado | Descripción |
|-----------|------|-------------|----------------|-------------|
| store | string | sí | — | Dominio de la tienda (p. ej., mitienda.myshopify.com) |
| filter | string | sí | — | Filtro de clientes (p. ej., `tag:vip`, `total_spent:>=500`) |
| note | string | sí | — | Texto de la nota que se agrega a los clientes que cumplen el filtro |
| append | bool | no | true | Agrega a la nota existente (true) o la reemplaza por completo (false) |
| dry_run | bool | no | true | Muestra los clientes que cumplen el filtro sin ejecutar mutaciones |
| format | string | no | human | Formato de salida: `human` o `json` |

## Seguridad

> ⚠️ Si `append: false`, se sobrescribe por completo la nota existente del cliente y las notas anteriores se pierden. El valor predeterminado es `append: true`, que agrega la nota de forma segura con la fecha al inicio. Ejecuta con `dry_run: true` para confirmar la lista de clientes antes de aplicar los cambios.

## Pasos

1. **OPERACIÓN:** `customers` — consulta
   **Entradas:** `query: <filter>`, `first: 250`, selecciona `id`, `displayName`, `note` y el cursor de paginación
   **Resultado esperado:** Los clientes que cumplen el filtro, con sus notas actuales; pagina hasta que `hasNextPage: false`

2. Arma la nota nueva: si `append: true`, pon `[AAAA-MM-DD] <note>` antes de la nota existente (separadas por un salto de línea); si `append: false`, reemplázala por `<note>`

3. **OPERACIÓN:** `customerUpdate` — mutación
   **Entradas:** `id: <customer_id>`, `note: <new_note>`
   **Resultado esperado:** `customer { id, note }`, `userErrors`

## Operaciones GraphQL

```graphql
# customers:query — validada con api_version 2025-01
query CustomersByFilter($query: String!, $after: String) {
  customers(first: 250, after: $after, query: $query) {
    edges {
      node {
        id
        displayName
        defaultEmailAddress {
          emailAddress
        }
        note
        tags
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
```

```graphql
# customerUpdate:mutation — validada con api_version 2025-01
mutation CustomerUpdateNote($input: CustomerInput!) {
  customerUpdate(input: $input) {
    customer {
      id
      displayName
      note
    }
    userErrors {
      field
      message
    }
  }
}
```

## Seguimiento de la sesión

**Claude DEBE mostrar lo siguiente en cada etapa. Es obligatorio.**

**Al iniciar**, muestra:
```
╔══════════════════════════════════════════════╗
║  SKILL: shopify-notas-clientes-masivas       ║
║  Tienda: <dominio de la tienda>              ║
║  Inicio: <AAAA-MM-DD HH:MM UTC>              ║
╚══════════════════════════════════════════════╝
```

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

Si `dry_run: true`, antepón `[SIMULACIÓN]` a cada paso de mutación y no lo ejecutes.

**Al terminar**, muestra:

Para `format: human` (predeterminado):
```
══════════════════════════════════════════════
RESUMEN DEL RESULTADO
  Clientes encontrados:  <n>
  Notas actualizadas:    <n>
  Errores:               <n>
  Archivo:               annotation_log_<fecha>.csv
══════════════════════════════════════════════
```

Para `format: json`, muestra:
```json
{
  "skill": "shopify-notas-clientes-masivas",
  "store": "<dominio>",
  "started_at": "<ISO8601>",
  "dry_run": true,
  "filter": "<filtro>",
  "note": "<texto>",
  "append": true,
  "outcome": {
    "matched": 0,
    "updated": 0,
    "errors": 0,
    "output_file": "annotation_log_<fecha>.csv"
  }
}
```

## Formato de salida
Archivo CSV `annotation_log_<AAAA-MM-DD>.csv` con las columnas:
`customer_id`, `name`, `email`, `previous_note`, `new_note`

## 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 |
| `userErrors` en customerUpdate | Datos no válidos o cliente de solo lectura | Registra el error, omite el cliente y sigue |
| Ningún cliente cumple el filtro | Filtro demasiado restringido | Termina con 0 coincidencias |

## Buenas prácticas
- Usa siempre `append: true`, a menos que de verdad quieras sobrescribir las notas existentes: las notas del equipo pueden guardar historial importante.
- Incluye la fecha dentro del propio texto de `note` (p. ej., `"2026-04-11: Participó en la campaña X"`) para que la nota se entienda meses después.
- Usa `dry_run: true` para confirmar cuántos clientes se van a anotar: un filtro amplio puede incluir miles de clientes sin que lo esperes.
- Si quieres registrar de dónde vino una importación, anota a los clientes justo después de importarlos para dejar un rastro de auditoría claro.
