# Guía de integración de Argumentum

Todo lo necesario para integrar Argumentum en **cualquier aplicación web** —
HTML plano, React, Vue, Angular o lo que venga — y sacarle valor desde el
primer día: analítica de uso (páginas, clicks, funnels, paths, adopción,
clientes/tenants) y guías in-app (tooltips, lightboxes, banners, checklists).

---

## 1. Instalación (5 minutos)

### 1.1 Crea la aplicación en el panel

Panel → **Apps e integración** → escribe el nombre de tu web → *Crear app*.
Copia la **app key** (`sk_…`). Cada web/bundle que instrumentes lleva su
propia key; todas comparten el mismo servidor y la misma base analítica.

### 1.2 Pega el snippet en tu web

En el `<head>` (o al final del `<body>`) de **todas** las páginas:

```html
<script src="https://TU-ARGUMENTUM/agent.js"
        data-key="sk_xxxxxxxxx"
        data-endpoint="https://TU-ARGUMENTUM"></script>
```

- `data-endpoint` es opcional si el agente se sirve del mismo origen que la
  API. En despliegues con dominio propio, será tu dominio.
- El agente es **first-party**: sírvelo desde tu dominio (o el del servidor)
  y no será bloqueado por ad-blockers ni pierde cookies de terceros.
- Sin más configuración ya está **auto-capturando**: páginas vistas y clicks
  (con selector resuelto), y auto-registrando páginas nuevas.

### 1.3 (Recomendado) Identifica a tus usuarios

Sin identificar, todo funciona con visitantes anónimos. Con `identify`
activas: segmentación de guías por rol/plan/lo-que-quieras, la vista de
**Clientes (tenants)** y filtros por usuario.

```js
// en el momento en que sabes quién es el usuario (tras login, bootstrap, etc.)
argumentum('identify', {
  visitorId: user.id,            // estable y único por usuario
  tenantId:  user.orgId,         // opcional: id de su empresa/cliente (SaaS multitenant)
  visitor:   { rol: user.role, idioma: user.locale },   // metadata de usuario
  tenant:    { plan: user.plan, sector: user.sector }   // metadata de la empresa
});

// eventos de negocio con propiedades
argumentum('track', 'proyecto_creado', { plantilla: 'marketing' });
argumentum('track', 'checkout_completado', { importe: 49.9, moneda: 'EUR' });
```

Reglas de la metadata:

- Los campos que envíes quedan disponibles para **segmentar** (audiencias de
  guías, filtros del panel, estimates de segmentos).
- Nunca envíes PII libre (email, DNI, teléfono) como valor de campo: usa ids
  o categorías. El esquema recomendado: `rol`, `idioma` (visitor) y `nombre`,
  `plan`, `sector`, `pais`, `fecha_alta` (tenant).
- `visitorId` debe ser **estable entre sesiones** (el id de tu base de datos).
  Si no lo envías, el agente genera uno anónimo en `localStorage`.

---

## 2. Etiquetado preciso (Pages y Features)

El tagging conecta el tráfico capturado con conceptos de producto. Es la base
de clickmaps, funnels, adopción y paths. Se gestiona en **Panel → Páginas y
features** (o por API, sección 5).

### 2.1 Pages: qué cuenta como página

Una page agrupa URLs bajo un nombre. Reglas posibles (una page puede tener
varias; **se evalúan en OR**, basta con que una encaje):

| Regla | Significado | Ejemplo | Encaja con |
|---|---|---|---|
| `exacta:/facturacion` | URL idéntica (path+query) | `/facturacion` | solo esa |
| `prefijo:/app/fact/` | empiece por | `/app/fact/` | `/app/fact/2026`, `/app/fact/lista` |
| `contiene:checkout` | contenga en cualquier posición | `checkout` | `/es/checkout/ok`, `/checkout` |
| `regex:^/fact/[0-9]+$` | expresión regular | `^/fact/[0-9]+$` | `/fact/123`, no `/fact/abc` |

Una cadena sin prefijo (`"checkout"`) equivale a `contiene:checkout`.

**Buenas prácticas**

1. **General después de específica**: el matcher devuelve la **primera** page
   cuyas reglas encajen (puedes reordenarlas). Crea primero las pages
   específicas (`exacta:/app/fact/nueva` → «Nueva factura») y al final la
   genérica (`prefijo:/app/fact/` → «Facturación»).
2. **Normaliza ids variables con regex**: para `/fact/1234` usa
   `regex:^/fact/[0-9]+$` en vez de `contiene:/fact/`, que también capturaría
   `/facturacion`.
3. **Excluye lo que no es producto**: en Apps → Ámbito de tracking puedes
   excluir rutas (`prefijo:/panel-admin`, `regex:^/legal/`). Lo excluido no
   genera datos.
4. **Auto-registro**: si llega tráfico de una ruta sin página, el servidor crea
   una page automática (`exacta:ruta`). Revísalas en Páginas y features y
   renómbralas o fusiónalas añadiendo su regla a otra page.

### 2.2 Features: qué elemento es cada cosa

Una feature es **un elemento concreto de la UI** (botón, enlace, pestaña,
toggle). Se define por selector CSS. La adopción, los clickmaps y los funnels
cuentan clicks que caen dentro del elemento seleccionado.

**Cómo escribir un selector que no se rompa** (por orden de preferencia):

1. **Data-atributo propio** — el más estable:
   `[data-sv='crear-proyecto']`, `[data-testid='exportar']`.
   Si controlas el frontend, añade `data-*` semánticos a los elementos
   importantes. Es la práctica que hace el tagging inmune a rediseños.
2. **`#id`** si es único y estable: `#btn-exportar`.
3. **`[aria-label='…']`** para botones icónicos accesibles.
4. **Ruta CSS corta** como último recurso: `nav > button:nth-of-type(2)`.
   Frágil ante cualquier cambio de maquetación: úsala solo como respaldo.

Cada feature admite **respaldos** (`selector.respaldo`): lista ordenada de
selectores alternativos. El agente usa el primero que encuentre en el DOM.

**Verifica siempre tu selector** antes de guardarlo, en la consola del
navegador sobre la página real:

```js
document.querySelectorAll("[data-sv='crear-proyecto']").length   // debe ser exactamente 1
```

(1 = perfecto; >1 = coge el primero visible; 0 = no existe en esa página).

**Captura con el modo diseñador**: en Páginas y features, escribe la URL de tu
web y pulsa 🎯: se abre tu web con el agente en modo selección; al hacer click
en un elemento, el selector se rellena en el formulario. También puedes abrir
directamente `https://tu-web?argumentum_designer=feature`.

**Detalles por framework** (el agente es vanilla JS y funciona igual en todos):

- **Vue**: los atributos `scoped` (`data-v-3f2a1b`) cambian entre builds —
  taggea con `data-*` propios, no con los generados.
- **React**: los `className` generados (CSS modules, styled-components tipo
  `Button-sc-1x2y3z`) cambian en cada build — usa `data-testid` o `id`.
- **Angular**: evita `_ngcontent-xxx`/`_nghost-xxx` (cambian por compilación);
  `data-*` propios o `aria-label`.
- Elementos dentro de **Shadow DOM**: el autocapture no los ve; usa
  `argumentum('track', …)` para ellos.

### 2.3 Track events (lo que no es un click)

Para hitos de negocio que no son interacciones de UI (compras, importaciones,
validaciones), usa `argumentum('track', nombre, props)`. Aparecen en la vista
Adopción/top como «eventos» y son pasos válidos de funnels y paths
(`tipo: "track"`).

---

## 3. Guías in-app

### 3.1 Crear y publicar

Panel → Guías → *Nueva guía*:

1. **Pasos**: cada paso tiene tipo (tooltip / lightbox / banner), título,
   cuerpo, texto del botón y **ancla** (selector CSS, opcional). Con «🎯
   Elegir ancla» el modo diseñador captura el selector sobre tu web real.
   - Paso sin ancla: tooltip centrado (o banner si es de tipo banner).
   - Ancla `obligatorio: true`: si el elemento no está en la página, el paso
     no se muestra (nunca molestar sin contexto).
2. **Audiencia**: reglas sobre la metadata (`tenant.plan es Enterprise`,
   `visitor.rol es admin`), páginas donde aparece (reglas de URL, vacío =
   todas) y frecuencia (`siempre` / `una_vez` / `sesión`).
3. **Programa**: fechas de visibilidad. No hay distinción de entornos: solo lo
   `publicada` llega al bundle (para probar en otra web, crea otra app con su
   propia key).
4. **Publicar**: regenera el bundle `GET /v1/config`; los usuarios lo reciben
   al recargar o navegar. Guardar como **borrador** o **staged** para pruebas.

El **throttle global** de la app (Ajustes: máx. guías por usuario y ventana,
por defecto 2/semana) evita saturar: si dos guías compiten por el mismo
usuario, gana la de menor `prioridad`.

Tipos: `tooltip` (anclado con flecha), `lightbox` (centrado con fondo),
`banner` (barra superior) y `checklist` (widget fijo con progreso; cada tarea
lanza su paso anclado y cuenta como completada).

### 3.2 Por API (sin panel)

```bash
curl -X PUT https://TU-ARGUMENTUM/v1/panel/guides/sk_xxx \
  -H "Authorization: Bearer ADMIN_TOKEN" -H "Content-Type: application/json" \
  -d '{
    "id": "g-onboarding-1",
    "nombre": "Tour de bienvenida",
    "tipo": "tooltip",
    "estado": "publicada",
    "prioridad": 1,
    "paginas": ["prefijo:/app/"],
    "frecuencia": { "tipo": "una_vez" },
    "segmentoReglas": [{ "campo": "visitor.rol", "operador": "es", "valor": "editor" }],
    "steps": [
      { "tipo": "tooltip", "contenido": { "titulo": "Bienvenido", "cuerpo": "3 pasos y fuera.", "cta": "Empezar" }, "anclaje": null },
      { "tipo": "tooltip", "contenido": { "titulo": "Crea tu primer proyecto", "cuerpo": "Con este botón.", "cta": "Siguiente" },
        "anclaje": { "selector": "[data-sv='crear']", "obligatorio": true } }
    ]
  }'
```

Estados: `borrador` → `staged` → `publicada` → `pausada` (DELETE elimina la
guía). Un PUT guarda el def completo y **sobrescribe lo anterior**: no hay
histórico de versiones (lo que se fue, se fue; usa borrador hasta tenerlo
claro). `POST /v1/panel/guides/:appKey/:id/publish` pasa la guía a `publicada`,
y el bundle `GET /v1/config` se regenera por ETag.

### 3.3 Métricas de guías

Impresiones, avances y completación por guía: vista Guías del panel o
`POST /v1/query/guias`. Eventos generados por el agente: `impresion`,
`siguiente`, `atras`, `cta`, `cierre`, `completo`.

---

## 4. Importar desde Pendo

1. **Conexión directa**: Panel → Guías → *Importar desde Pendo* → pega la
   integration key → el servidor descarga guías, pages, features y metadata →
   selecciona qué importar.
2. **CLI**: `node tools/pendo-export/export.mjs --key KEY` (volcado) y
   `node tools/pendo-import.mjs --app-key sk_xxx --dir pendo-export-…`.

Detalle del mapeo, estados y deduplicación: sección «Importar desde Pendo»
del README y `docs/pruebas-finales.md`.

---

## 5. API de consulta (BI, informes, tu propio código)

Autenticación: `Authorization: Bearer ADMIN_TOKEN` (o el token de sesión del
panel). Todas aceptan
`appKey` (obligatorio), `tenantId` (opcional, filtra a un cliente),
`rango` (`24h|7d|30d|12w|6m`; un valor desconocido cae al defecto `12w`) y
`from`/`to` ISO explícitos.

| Función (`POST /v1/query/…`) | Parámetros extra | Devuelve |
|---|---|---|
| `mau` | `granularity: day\|week\|month` | serie `{periodo, usuarios}` |
| `kpis` | — | usuarios, sesiones, pageviews, clicks, tenants |
| `top` | `kind: pages\|features\|tracks`, `limit` | ranking con usuarios y usos |
| `funnel` | `pasos: [{tipo, id}]` (≥2), `ventanaHoras`, `ventanaModo: inicio\|paso`, `desglose: {campo}` | usuarios, % y tiempo hasta paso (mediana/media) por nivel; la ventana puede medir desde el primer paso o entre pasos; con `desglose` un funnel por valor del campo, visitantes sin identify en `(sin valor)` |
| `paths` | `evento {tipo,id}`, `direccion: antes\|despues`, `niveles` | niveles con distribución y salidas |
| `arbol` | `profundidad` (def. 4), `topHijos` (def. 8) | mapa de uso de páginas: árbol de navegación desde la página de entrada, rol de cada página (tránsito, contenido, salida, agujero negro), permanencia mediana, clicks/vista y % salida |
| `clickmap` | `pageId`, `cell` (%), `dispositivo: desktop\|tablet\|movil`, `doc: true` | celdas con conteo + desglose por feature; `doc` usa la posición en el documento (yd) para el mapa de página completa |
| `clickmap-elementos` | `pageId`, `dispositivo`, `limit` | ranking de elementos clicados (feature o selector): clicks, usuarios, rage clicks y último uso |
| `clickmap-detail` | `pageId`, `dispositivo` | detalle temporal de los clicks de una página: total, usuarios, serie diaria, por hora y día de semana, zonas por feature |
| `feature-detail` | `featureId` | detalle temporal de usos de una feature: total, usuarios, primero/último, serie diaria, horas y día de semana |
| `scroll` | `pageId` | profundidad de scroll: sesiones, media % y tramos ≤25/26–50/51–75/76–100 |
| `adoption` | — | usuarios, usos, tendencia semanal por feature |
| `guias` | — | impresiones/avances/completados por guía |
| `retencion` | `semanas` (def. 12, máx. 26) | cohorts semanales: matriz de usuarios activos por offset (celda nula = semana aún no llegada) |
| `nps` | — | por encuesta: respuestas, promotores/pasivos/detractores, score NPS, sí/no y comentarios recientes |
| `encuestas` | — | respuestas por pregunta: tipo de escala, NPS, media 1–5, sí/no, distribución de opciones, comentarios y pregunta de abandono |
| `feedback` | — | por ruta/pantalla: total, % de útiles y comentarios recientes (widget de ayuda de la web) |
| `errores` | — | top de errores JavaScript por mensaje: ocurrencias, usuarios y último visto |
| `formularios` | — | por formulario: iniciados, enviados, abandonados, campos y tasa de envío |
| `tenants` | — | por tenant: usuarios, WAU, delta, features, attrs |

Ejemplo con `fetch`:

```js
const r = await fetch("https://TU-ARGUMENTUM/v1/query/funnel", {
  method: "POST",
  headers: { "Content-Type": "application/json", Authorization: "Bearer " + TOKEN },
  body: JSON.stringify({
    appKey: "sk_xxx", tenantId: "cliente-42", rango: "30d",
    ventanaHoras: 168,                       // conversión dentro de 7 días
    ventanaModo: "inicio",                   // o "paso": ventana entre pasos consecutivos
    pasos: [
      { tipo: "page",    id: "/app/inicio" },
      { tipo: "feature", id: "f-crear" },
      { tipo: "track",   id: "proyecto_creado" }
    ]
  })
});
```

Ingesta server-side (eventos que no pasan por el navegador: backend, ERP…):

```bash
curl -X POST https://TU-ARGUMENTUM/v1/events -H "X-Api-Key: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{"lote":[{"tipo":"track","ts":1760000000000,"visitorId":"u-1",
       "tenantId":"cliente-42","path":"/api/proceso","trackName":"import_completa"}]}'
```

Smoke test completo de un despliegue:
`ADMIN_TOKEN=… node tools/api-smoke.mjs --server https://TU-ARGUMENTUM`

---

## 6. Resolución de problemas

| Síntoma | Causa probable | Solución |
|---|---|---|
| No llega ningún evento | key errónea, endpoint bloqueado, ruta excluida por el ámbito | consola del navegador → red; revisa `data-key` y el ámbito en Ajustes |
| Una feature cuenta 0 | selector roto o que matchea 0/2+ elementos | verifícalo con `document.querySelectorAll(...)` en la página |
| Adopción repentina a 0 tras un despliegue | el selector cambió con la build | añade `data-*` estables y usa `selector.respaldo` |
| Cambio de ruta SPA no genera pageview | router poco común que no toca History API ni hash | revisa el router; History API y hash-change están soportados nativamente |
| La guía no aparece | frecuencia ya consumida, throttle, fuera de fechas, ancla obligatoria ausente, segmento no cumple | revisa esos puntos; limpia `localStorage.sv_seen` para probar; recuerda que solo el estado `publicada` llega al bundle |
| 401 en el panel | usuario o contraseña incorrectos, sesión expirada (24 h) o usuario desactivado | vuelve a hacer login con tu usuario y contraseña; el `ADMIN_TOKEN` solo vale por API (`Authorization: Bearer`) y en el login como acceso de servicio |

---

## 7. Seguridad y privacidad (producción)

- Fija siempre `ADMIN_TOKEN` fuerte (el token de desarrollo solo avisa en log).
- La ingesta autentica con la app key (pública por diseño, como cualquier
  herramienta client-side); el rate de ingesta lo fija la cuota de la
  organización de la app (`reqPorMinuto`, 600/min por defecto, `0` = sin
  límite) y frena abusos.
- `HTTPS` obligatorio vía tu proxy inverso; el agente no envía cookies.
- No se capturan valores de inputs (diseño del agente, no desactivable).
- Datos en TU infraestructura; sin terceros. Exporta o borra a tu criterio
  (los eventos son filas de ClickHouse).
