v1.0

boltBitnodo Analytics API

Esta API permite registrar usuarios, crear portales, insertar un widget unico de rastreo y consultar eventos/alertas en tiempo casi real para sistemas propios o de terceros.

Base URL Produccion

https://analytics.bitnodo.net/api/v1

Formato

Content-Type: application/json

Plan Portales maximos Capacidades
free 1 Vista cliente con visitas, tiempo real, paginas visitadas, tipo de cliente y metricas basicas
pro 50 Todo lo de free + visibilidad de tracking avanzado, event_payload completo y leads

La captura en background se realiza completa para todos los planes. Lo que cambia por plan es la informacion visible para el cliente final en los endpoints de consulta.

Quickstart para integrador

  1. Desarrolladores crea una superclave temporal para el cliente.
  2. Registrar cuenta y usuario con POST /auth/register + super_key.
  3. Guardar access_token, user.id, account.id y api_key.
  4. Crear portal con POST /portals usando Bearer token.
  5. Incrustar el widget JS con data-key y data-tenant (modo recomendado) o usar modo manual con companyId/portalId.
  6. Consumir alertas y eventos con X-API-Key.
curl -X POST "https://analytics.bitnodo.net/api/v1/auth/register" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "owner@empresa.com",
    "password": "SuperPassword123!",
    "full_name": "Owner Demo",
    "account_name": "Empresa Demo",
    "super_key": "ABCD2345EFGH6789IJKL2345MNOP6789"
  }'

Aprobacion por superadmin

El alta de integradores no es libre. Cada registro requiere una super_key temporal emitida por Bitnodo. Esta clave es alfanumerica segura, de un solo uso y se marca como consumida inmediatamente cuando la cuenta se crea.

La super_key aplica solo al alta inicial de la cuenta/empresa. La creacion, edicion y eliminacion de portales se realiza con Bearer token y no requiere superclave.

Reglas de superclave

  • Formato alfanumerico seguro
  • Caducidad por tiempo (TTL)
  • Uso unico
  • No se almacena en claro, solo hash

Provisionamiento controlado

La emision de superclaves es gestionada internamente por Bitnodo y no forma parte de la API publica para integradores.

Autenticacion

Metodo Endpoint Uso Auth
POST /auth/register Crea usuario + cuenta + api_key + token (requiere super_key) No
POST /auth/login Entrega access_token y datos del usuario No
GET /auth/me Retorna perfil autenticado e ids Bearer token
POST /auth/logout Invalida token actual Bearer token
curl "https://analytics.bitnodo.net/api/v1/auth/me" \
  -H "Authorization: Bearer TU_ACCESS_TOKEN"

Ejemplo JavaScript — login y autenticacion

// 1. Login y obtener access_token
const loginRes = await fetch('https://analytics.bitnodo.net/api/v1/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'owner@empresa.com', password: 'SuperPassword123!' })
});
const { access_token, user, account } = await loginRes.json();
// Guardar para uso posterior:
// access_token → Bearer token para portales / configuracion
// account.api_key → X-API-Key para lectura de alertas / visitas

// 2. Verificar sesion activa
const meRes = await fetch('https://analytics.bitnodo.net/api/v1/auth/me', {
  headers: { 'Authorization': `Bearer ${access_token}` }
});
const profile = await meRes.json();
console.log(profile.user.id, profile.account.id, profile.tenant.id);

Gestion de portales (CRUD)

Un usuario autenticado puede crear multiples portales/paginas para monitoreo usando su Bearer token. Este flujo no utiliza super_key.

Limites por plan: free permite 1 portal; pro permite hasta 50 portales.

Metodo Endpoint Uso
GET /portals Lista portales de la cuenta autenticada
POST /portals Crea portal (name, domain)
PUT /portals/{id} Edita name, domain, is_active
DELETE /portals/{id} Elimina portal
curl -X POST "https://analytics.bitnodo.net/api/v1/portals" \
  -H "Authorization: Bearer TU_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Sitio principal","domain":"www.empresa.com"}'

Ejemplo JavaScript — crear y listar portales

const headers = {
  'Authorization': 'Bearer TU_ACCESS_TOKEN',
  'Content-Type':  'application/json'
};

// Crear portal
const createRes = await fetch('https://analytics.bitnodo.net/api/v1/portals', {
  method: 'POST',
  headers,
  body: JSON.stringify({ name: 'Sitio principal', domain: 'www.empresa.com' })
});
const { portal } = await createRes.json();
console.log('Portal ID:', portal.id); // Guardar para widget y tracking

// Listar portales
const listRes = await fetch('https://analytics.bitnodo.net/api/v1/portals', { headers });
const { portals } = await listRes.json();

Reglas de intencion por portal

Cada portal puede configurar su propio scoring de intencion mediante intent_rules. Si un portal no define reglas personalizadas, el sistema usa reglas por defecto globales.

Metodo Endpoint Uso
GET /portals/{id}/intent-rules Obtiene reglas actuales y thresholds
POST /portals/{id}/intent-rules Crea reglas personalizadas del portal
PUT /portals/{id}/intent-rules Actualiza reglas y thresholds
DELETE /portals/{id}/intent-rules Elimina reglas personalizadas y vuelve a defaults
{
  "rules_json": {
    "pageview": 1,
    "scroll_depth": {"50": 1, "75": 2, "100": 3},
    "link_click": 2,
    "video_play": 2,
    "file_download": 4,
    "form_submit": 10,
    "commercial_url_bonus": 5
  },
  "threshold_interested": 6,
  "threshold_high": 11,
  "threshold_very_high": 21
}

Widget unico de rastreo

Desde esta iteracion, el panel de Bitnodo puede generar automaticamente el snippet de instalacion consultando una configuracion publica por tenant.

Flujo de instalacion: panel -> API -> JSON de configuracion -> snippet -> cliente copia y pega.

<script
  src="https://analytics.bitnodo.net/widget/tracker.js"
  data-key="pk_live_xxxxxxxxx"
  data-tenant="23"
></script>
El widget lee data-key y data-tenant. Con esos datos consulta GET /v1/widget/config/{tenant_id} y aplica configuracion dinamica (consentimiento, tracking y claves publicas) sin hardcodear reglas en el panel.
Importante: tracker.js inicia con data-tenant. El atributo data-portal por si solo no inicializa el widget.

Configuracion JSON de widget por tenant

Metodo Endpoint Auth Uso
GET /v1/widget/config/{tenant_id} No Devuelve configuracion publica para generar snippet e inicializar widget
curl "https://analytics.bitnodo.net/v1/widget/config/23"
{
  "tenant": {
    "id": "23",
    "name": "Nombre del sitio"
  },
  "widget": {
    "enabled": true,
    "script_url": "https://analytics.bitnodo.net/widget/tracker.js",
    "public_key": "pk_live_xxxxxxxxx",
    "company_id": "ACCOUNT_ID_UNICO",
    "portal_id": "PORTAL_ID_OPCIONAL",
    "snippet": ""
  },
  "consent": {
    "enabled": true,
    "type": "modal",
    "storage": "localStorage",
    "text": "Este sitio utiliza herramientas de analisis de comportamiento para mejorar nuestros servicios y detectar oportunidades comerciales.",
    "terms_title": "Terminos de analisis de datos",
    "terms_body": "Este sitio utiliza Bitnodo Analytics para analizar el comportamiento de navegacion y detectar oportunidades comerciales."
  },
  "tracking": {
    "page_views": true,
    "scroll_tracking": true,
    "click_tracking": true
  }
}

Integracion con panel

  • El panel consume el endpoint de config por tenant.
  • Con la respuesta genera snippet corto y snippet extendido.
  • El cliente final solo copia y pega el codigo en su sitio.

Tablas involucradas

  • tenants: identidad del tenant y public_key.
  • widget_config: consentimiento y toggles de tracking.
  • accounts: company_id publico para /track.
<!-- Snippet extendido opcional generado por panel -->
<script src="https://analytics.bitnodo.net/widget/tracker.js"
  data-key="pk_live_xxxxxxxxx"
  data-tenant="23"
  data-api-base="https://analytics.bitnodo.net/api/v1"
  data-config-base="https://analytics.bitnodo.net"></script>
Seguridad: este endpoint solo expone configuracion publica del widget. Nunca retorna secret_key, tokens internos ni credenciales privadas.
<!-- Modo manual (fallback) -->
<script src="https://analytics.bitnodo.net/widget/tracker.js"></script>
<script>
  window.BitnodoAnalytics.init({
    apiBase: "https://analytics.bitnodo.net/api/v1",
    companyId: "ACCOUNT_ID_UNICO",
    portalId: "PORTAL_ID_UNICO",
    widgetVersion: "1.0",
    captureFormContacts: true,
    legalMessage: "Este sitio usa analitica para mejorar la experiencia de usuario."
  });
</script>

Que monitorea el widget

El widget de Bitnodo Analytics esta pensado para operar como una capa de observacion del trafico web, similar al enfoque de herramientas de analytics. Detecta apertura de pagina, permanencia activa, navegacion interna y salida del visitante, y transforma esas acciones en eventos que la API puede almacenar y procesar.

Evento Cuando ocurre Uso analitico
pageview Cuando la pagina carga y el widget se inicializa Contabilizar visitas, paginas vistas y origen del trafico
heartbeat Cada intervalo configurado mientras la pestana esta activa Medir tiempo real de atencion y permanencia efectiva
scroll_depth Al alcanzar hitos de scroll como 25%, 50%, 75% o 100% Medir lectura real, interes por contenido y profundidad de consumo
link_click Cuando el visitante hace clic en un enlace Analizar navegacion interna y salidas hacia sitios externos
video_play Cuando se reproduce un video HTML5 dentro de la pagina Medir interaccion audiovisual y consumo de contenido multimedia
file_download Cuando se detecta clic en un enlace de descarga Auditar descargas de documentos, recursos o archivos comerciales
form_submit Cuando se envia un formulario Medir conversiones, leads y puntos de interaccion clave; puede incluir email y telefono
unload Cuando el usuario abandona la pagina o la recarga Registrar cierre de sesion/pagina y capturar ultima navegacion conocida

Monitoreo de navegacion

  • URL actual visitada
  • Pagina anterior por referrer
  • Siguiente URL estimada si el usuario navega dentro del mismo sitio
  • Frecuencia de actividad durante la sesion
  • Profundidad de scroll por hitos
  • Clics en enlaces y descargas
  • Envio de formularios
  • Reproduccion de videos HTML5
  • Captura de email y telefono al enviar formularios

Monitoreo de contexto tecnico

  • IP publica del visitante
  • Navegador detectado a partir del User-Agent
  • Pais de conexion
  • Proveedor de internet o ISP
  • Idioma y zona horaria del navegador
En terminos practicos, el widget funciona como una sonda ligera de analitica: observa el comportamiento basico del visitante en la pagina y lo envia a Bitnodo para monitoreo, consulta por API y generacion de alertas.

Datos recolectados

La API recibe estos datos desde el widget para construir una vision operativa del trafico, comparable a una implementacion base de analytics orientada a visitas, procedencia, permanencia y navegacion dentro del sitio.

Datos tecnicos capturados

  • IP de conexion
  • User-Agent y navegador detectado
  • Pais de conexion
  • ISP / compania de internet
  • Pagina actual
  • Referrer (pagina anterior)
  • Siguiente URL estimada en misma pestana
  • Tiempo activo en pantalla
  • Idioma y zona horaria
  • Payload JSON de interaccion segun tipo de evento
  • Email y telefono cuando el formulario se envia con esos campos presentes

Endpoint de ingreso de eventos

POST /track y POST /track/batch

{
  "companyId": "ACCOUNT_ID_UNICO",
  "portalId": "PORTAL_ID_UNICO",
  "visitorId": "vtr_9j2k3lm...",
  "sessionId": "ses_c4d5e6f...",
  "eventType": "pageview",
  "pageUrl": "https://www.empresa.com/home",
  "referrer": "https://google.com/",
  "nextUrl": "https://www.empresa.com/precios",
  "screenActiveSeconds": 43,
  "timezone": "America/Santiago",
  "language": "es-CL",
  "widgetVersion": "1.0",
  "eventData": {
    "action": "https://www.empresa.com/contacto",
    "method": "POST",
    "fieldCount": 4,
    "lead": {
      "email": "cliente@empresa.com",
      "phone": "+56912345678",
      "emailField": "email",
      "phoneField": "telefono"
    }
  }
}
Rate limit real de ingesta: maximo 100 req/min por IP y 2000 eventos/min por portal. Si se excede, la API responde HTTP 429 con error: rate_limited.
Compresion soportada: la API acepta Content-Encoding: gzip en /track y /track/batch para reducir trafico de red.
{
  "events": [
    {
      "companyId": "ACCOUNT_ID_UNICO",
      "portalId": "PORTAL_ID_UNICO",
      "visitorId": "vtr_9j2k3lm...",
      "sessionId": "ses_c4d5e6f...",
      "eventType": "pageview",
      "pageUrl": "https://www.empresa.com/home",
      "eventData": null
    },
    {
      "companyId": "ACCOUNT_ID_UNICO",
      "portalId": "PORTAL_ID_UNICO",
      "visitorId": "vtr_9j2k3lm...",
      "sessionId": "ses_c4d5e6f...",
      "eventType": "scroll_depth",
      "pageUrl": "https://www.empresa.com/home",
      "eventData": {"milestone":75}
    }
  ]
}
Anti-spam en formularios: el sistema descarta leads sospechosos con honeypot, tiempos de envio demasiado rapidos y repeticion agresiva de contactos.

Ejemplo JavaScript — enviar eventos

const BASE = 'https://analytics.bitnodo.net/api/v1';

// Enviar evento individual
await fetch(`${BASE}/track`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    companyId:           'TU_ACCOUNT_ID',
    portalId:            'TU_PORTAL_ID',
    visitorId:           'vtr_' + Math.random().toString(36).slice(2),
    sessionId:           'ses_' + Date.now().toString(36),
    eventType:           'pageview',
    pageUrl:             window.location.href,
    referrer:            document.referrer,
    screenActiveSeconds: 0,
    timezone:            Intl.DateTimeFormat().resolvedOptions().timeZone,
    language:            navigator.language,
    widgetVersion:       '1.0'
  })
});

// Enviar batch de eventos acumulados
await fetch(`${BASE}/track/batch`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    events: [
      { companyId: 'TU_ACCOUNT_ID', portalId: 'TU_PORTAL_ID',
        visitorId: 'vtr_abc', sessionId: 'ses_001',
        eventType: 'scroll_depth', pageUrl: window.location.href,
        eventData: { milestone: 75 } },
      { companyId: 'TU_ACCOUNT_ID', portalId: 'TU_PORTAL_ID',
        visitorId: 'vtr_abc', sessionId: 'ses_001',
        eventType: 'link_click', pageUrl: window.location.href,
        eventData: { href: 'https://www.miempresa.com/precios' } }
    ]
  })
});

Pipeline de procesamiento de eventos

La API registra cada evento en la tabla events para desacoplar ingestion y procesamiento. El flujo queda preparado para workers de alto volumen.

Widget
  -> POST /track
          -> POST /track/batch (opcional)
  -> events (queue)
  -> processor interno / worker
  -> visit aggregation
  -> intent score
  -> alerts
  -> marketplace leads
Estructura base de events: portal_id, company_id, visit_id, event_type, payload_json, created_at, processed_at.

Deteccion de intencion de compra

Bitnodo Analytics calcula un intent_score por sesion de visita (visit_id) usando los eventos del widget. Esto permite detectar en tiempo casi real visitas con alto interes comercial.

Como funciona

  • Los eventos se agrupan por sesion con visit_id (ventana de 30 minutos)
  • Se acumula score por comportamiento de navegacion
  • Se clasifica el nivel de intencion en normal/interested/high/very_high
  • Si el score supera el umbral, se crea alerta high_intent_visit

Reglas base de score

  • pageview +1
  • scroll_depth 50/75/100 = +1/+2/+3
  • heartbeat mayor a 60s y 120s = +2/+4
  • link_click +2, video_play +2, file_download +3
  • form_submit +10
  • URL comercial (precios, demo, cotizar, etc.) +5
El scoring se puede desactivar por portal y soporta reglas personalizadas mediante intent_rules.

Visitas agregadas y sesiones

Cada evento se asocia a una sesion de visita (visit_id) y a dos identificadores del cliente: visitor_id (persistente) y session_id (rotacion cada 30 minutos). Esto mejora consistencia de scoring y agregacion multi-evento sin romper compatibilidad de endpoints existentes.

Campos operativos de visita

  • visit_id, portal_id, ip, current_page
  • country, device_type
  • source_type: direct/google/facebook/linkedin/twitter/referral
  • pages (paginas vistas por sesion)
  • duration_seconds (duracion total de sesion)
  • intent_score y intent_level

Endpoint

GET /visits?limit=50

curl "https://analytics.bitnodo.net/api/v1/visits?limit=50" \
  -H "X-API-Key: TU_API_KEY"
{
  "items": [
    {
      "visit_id": "vst_98ad91",
      "portal_id": "PORTAL_ID",
      "ip": "186.10.25.44",
      "current_page": "https://www.empresa.com/precios",
      "pages": 4,
      "duration_seconds": 132,
      "intent_score": 9,
      "intent_level": "interested",
      "country": "Chile",
      "device_type": "mobile",
      "source_type": "google",
      "company": {
        "name": "Empresa Demo SpA",
        "domain": "empresa.cl",
        "confidence": 0.82
      },
      "created_at": "2026-03-11 15:40:12"
    }
  ]
}

Obtener visita actual (sesión específica)

Endpoint para obtener los detalles y tiempo en sitio de una sesión específica (visit_id). Útil para paneles que necesitan mostrar el tiempo en sitio de un usuario actualmente navegando, sin sumar sesiones históricas previas.

Endpoint

GET /visits/{visit_id}

curl "https://analytics.bitnodo.net/api/v1/visits/vst_98ad91" \
  -H "X-API-Key: TU_API_KEY"

Parámetro requerido

  • visit_id (en URL): ID único de la sesión

Devuelve todos los campos disponibles: visit_id, duration_seconds, active_seconds_total, pages, session_id, etc.

{
  "visit_id": "vst_98ad91",
  "portal_id": "PORTAL_ID",
  "ip": "186.10.25.44",
  "current_page": "https://www.empresa.com/precios",
  "first_page": "https://www.empresa.com/",
  "last_page": "https://www.empresa.com/precios",
  "pages": 4,
  "duration_seconds": 125,
  "active_seconds_total": 85,
  "event_count": 12,
  "intent_score": 9,
  "intent_level": "interested",
  "country": "Chile",
  "device_type": "mobile",
  "source_type": "google",
  "session_id": "sess_7f4e2a",
  "company": {
    "name": "Empresa Demo SpA",
    "domain": "empresa.cl",
    "confidence": 0.82
  },
  "created_at": "2026-03-11 15:40:12",
  "updated_at": "2026-03-11 15:42:17"
}
Caso de uso: Cuando el panel muestra un usuario actualmente navegando, consulta este endpoint cada 5-10 segundos con el visit_id actual para obtener duration_seconds (tiempo real en sitio de esa sesión), evitando sumar tiempos de sesiones históricas previas.

Ejemplo JavaScript — obtener tiempo en sitio actual

Polling en el panel cada 5-10 segundos para mostrar el tiempo en sitio actual del usuario que está navegando ahora, sin sumar sesiones históricas.

// Obtener visita actual y mostrar tiempo en sitio real
const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://analytics.bitnodo.net/api/v1';
let currentVisitId = 'vst_98ad91'; // Obtenido del tracker.js widget

async function getTimeOnSite(visitId) {
  try {
    const response = await fetch(`${BASE_URL}/visits/${visitId}`, {
      headers: {
        'X-API-Key': API_KEY,
      }
    });

    if (!response.ok) {
      console.error(`Error fetching visit: ${response.status}`);
      return null;
    }

    const data = await response.json();
    return data; // Contiene: duration_seconds, active_seconds_total, pages, etc.
  } catch (error) {
    console.error('Error:', error);
    return null;
  }
}

// Formatear segundos a HH:MM:SS
function formatDuration(seconds) {
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = seconds % 60;
  return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
}

// Actualizar panel cada 5 segundos
setInterval(async () => {
  const visitData = await getTimeOnSite(currentVisitId);
  
  if (visitData) {
    // Actualizar elemento del panel
    document.getElementById('time-on-site').innerText = formatDuration(visitData.duration_seconds);
    document.getElementById('pages-viewed').innerText = visitData.pages;
    document.getElementById('intent-level').innerText = visitData.intent_level || 'N/A';
    document.getElementById('current-page').innerText = visitData.current_page;
  }
}, 5000);
Ventajas:
  • ✓ Muestra el tiempo de esta sesión solamente (no acumula históricas)
  • ✓ Siempre actualizado cada 5-10 segundos (usuario ve tiempo en tiempo real)
  • ✓ Incluye todos los datos de la visita: intención, páginas, fuente, empresa, etc.
  • ✓ Ideal para dashboards de monitoreo en vivo

Deteccion de empresas visitantes (B2B Visitor Intelligence)

Bitnodo intenta identificar si una visita proviene de una red corporativa usando la IP publica, metadatos tecnicos y resolucion por dominio. Esta deteccion no identifica personas, solo contexto empresarial de red.

Flujo de deteccion

  1. Se captura la IP del evento y se asocia a visit_id.
  2. Se consulta cache local ip_company_cache (TTL 7 dias).
  3. Se buscan rangos IP en company_ip_ranges.
  4. Si no hay match, se intenta reverse DNS.
  5. Para visitas con intent_score >= 8, se permite enriquecimiento adicional.

Datos B2B guardados en visita

  • company_id
  • company_name
  • company_domain
  • company_confidence

Metodo de deteccion registrado: ip_range, reverse_dns, external_enrichment o cache.

Alertas y monitoreo por API key

Para consultar alertas/eventos debes enviar la cabecera X-API-Key: TU_API_KEY.

Metodo Endpoint Resultado
GET /alerts?limit=20 Alertas recientes (ej: visit.new)
GET /events/recent?limit=50 Eventos recientes (en free vista basica, en pro incluye metadatos avanzados en event_payload)
GET /visits?limit=50 Visitas agregadas por sesion con ip, current_page, duracion, score, fuente de trafico y empresa detectada
GET /companies/detected?limit=50 Resumen de empresas detectadas, visitas, ultima actividad e intent score maximo
GET /leads?limit=50 Leads detectados desde formularios con email y/o telefono (solo plan pro)
GET /stats/overview Resumen agregado para dashboards (visitas, leads, top pages)
GET /visits/high-intent?limit=50 Visitas con mayor probabilidad comercial segun intent_score (plan pro)
curl "https://analytics.bitnodo.net/api/v1/alerts?limit=20" \
  -H "X-API-Key: TU_API_KEY"
{
          "event": "visit.company_detected",
          "portal_id": "PORTAL_ID",
          "visit_id": "VISIT_ID",
          "company_name": "Empresa Demo SpA",
          "company_domain": "empresa.cl",
          "intent_score": 14,
          "page_url": "/precios",
          "timestamp": "2026-03-11T18:20:00Z"
        }

Realtime interno (superadmin)

Para paneles operativos de tiempo real se recomienda usar el endpoint interno /internal/superadmin/realtime/overview con ventana corta y polling rapido. Este endpoint requiere cabecera X-Superadmin-Secret.

curl "https://analytics.bitnodo.net/internal/superadmin/realtime/overview?account_id=ACCOUNT_ID&portal_id=PORTAL_ID&since_seconds=30&active_window_seconds=20&active_by=session&limit=100" \
  -H "X-Superadmin-Secret: TU_SUPERADMIN_SECRET"

Parametros recomendados

  • since_seconds: ventana general del feed (recomendado: 30)
  • active_window_seconds: ventana de usuarios activos (recomendado: 15-30)
  • active_by: criterio de activos (visit, session, ip)
  • limit: cantidad maxima de items en events/visits

Campos realtime

  • realtime.active_visitors: metrica principal segun active_by
  • realtime.active_visits: visitas activas unicas
  • realtime.active_sessions: sesiones activas unicas
  • realtime.active_unique_ips: IPs unicas activas
  • realtime.active_window_seconds: ventana aplicada
Recomendacion para panel: polling cada 5-10 segundos para que la salida de un visitante se refleje de forma casi inmediata.

Ejemplo JavaScript — polling realtime interno

const BASE_INTERNAL = 'https://analytics.bitnodo.net/internal/superadmin';
const SUPERADMIN_SECRET = 'TU_SUPERADMIN_SECRET';

const params = new URLSearchParams({
  account_id: 'TU_ACCOUNT_ID',
  portal_id: 'TU_PORTAL_ID',
  since_seconds: '30',
  active_window_seconds: '20',
  active_by: 'session',
  limit: '100'
});

async function loadRealtimeOverview() {
  const url = `${BASE_INTERNAL}/realtime/overview?${params.toString()}`;
  const res = await fetch(url, {
    headers: {
      'X-Superadmin-Secret': SUPERADMIN_SECRET
    }
  });

  if (!res.ok) {
    throw new Error(`Realtime request failed: ${res.status}`);
  }

  const payload = await res.json();
  const data = payload.data || {};
  const realtime = data.realtime || {};

  // Metrica principal para tarjeta de "activos ahora"
  const activeNow = Number(realtime.active_visitors || 0);

  // Metricas secundarias utiles para diagnostico
  const sessions = Number(realtime.active_sessions || 0);
  const ips = Number(realtime.active_unique_ips || 0);

  console.log('[Realtime]', {
    activeNow,
    sessions,
    ips,
    activeBy: realtime.active_by,
    activeWindowSeconds: realtime.active_window_seconds
  });

  // Ejemplo: actualizar UI
  // document.querySelector('#active-now').textContent = String(activeNow);
  // document.querySelector('#active-sessions').textContent = String(sessions);
  // document.querySelector('#active-ips').textContent = String(ips);
}

async function pollRealtime() {
  try {
    await loadRealtimeOverview();
  } catch (error) {
    console.error(error);
  }
}

// Primera carga inmediata
pollRealtime();

// Polling recomendado: cada 5 segundos
setInterval(pollRealtime, 5000);

Ejemplo JavaScript — polling de alertas

const API_KEY = 'TU_API_KEY';
const BASE    = 'https://analytics.bitnodo.net/api/v1';

// Leer alertas recientes
const alertsRes = await fetch(`${BASE}/alerts?limit=20`, {
  headers: { 'X-API-Key': API_KEY }
});
const { items: alerts } = await alertsRes.json();

// Leer visitas con intent score
const visitsRes = await fetch(`${BASE}/visits?limit=50`, {
  headers: { 'X-API-Key': API_KEY }
});
const { items: visits } = await visitsRes.json();

// Polling cada 30 segundos para dashboard en tiempo real
setInterval(async () => {
  const res = await fetch(`${BASE}/alerts?limit=5`, {
    headers: { 'X-API-Key': API_KEY }
  });
  const data = await res.json();
  data.items.forEach(alert => {
    if (alert.intent_score >= 11) {
      console.log('Alta intencion:', alert.company_name, alert.intent_score);
    }
  });
}, 30_000);

Estadisticas agregadas para dashboard

El endpoint de overview evita procesar eventos crudos en clientes externos y entrega metricas listas para paneles operativos.

curl "https://analytics.bitnodo.net/api/v1/stats/overview" \
  -H "X-API-Key: TU_API_KEY"
{
  "visits_today": 124,
  "visits_yesterday": 110,
  "leads_today": 6,
  "high_intent_today": 9,
  "top_pages": ["/", "/servicios", "/precios"]
}
Limite operativo de lectura: hasta 60 requests por minuto por API key.
curl "https://analytics.bitnodo.net/api/v1/companies/detected?limit=50" \
  -H "X-API-Key: TU_API_KEY"
{
  "items": [
    {
      "company_name": "Empresa Demo SpA",
      "domain": "empresa.cl",
      "visits": 4,
      "last_visit": "2026-03-11 17:40:00",
      "highest_intent_score": 18
    }
  ]
}

Webhooks para integraciones externas

Puedes registrar webhooks por cuenta para recibir eventos operativos en tiempo real. Los intentos fallidos se reintentan automaticamente.

Metodo Endpoint Uso
GET /webhooks Lista webhooks configurados
POST /webhooks Crea webhook para visit.new, visit.high_intent, lead.created o lead.marketplace.created
PUT /webhooks/{id} Actualiza target_url, event_type o estado
DELETE /webhooks/{id} Elimina webhook
{
  "event": "lead.created",
  "portal_id": "PORTAL_ID",
  "visit_id": "VISIT_ID",
  "email": "cliente@empresa.com",
  "phone": "+56912345678",
  "page_url": "/contacto",
  "timestamp": "2026-03-11T17:20:00Z"
}

Lead Marketplace

El marketplace permite publicar oportunidades comerciales anonimizadas y desbloquear contacto completo solo despues de una compra valida del lead.

Tablas nuevas

  • marketplace_leads: oportunidades, estado, precio, cupos y contacto protegido
  • lead_purchases: compras efectivas por cliente
  • marketplace_subscriptions: cuentas suscritas por categoria/ciudad para notificaciones

Estructura clave

marketplace_leads guarda: portal_id, visit_id, category, sub_category, city, intent_score, lead_quality, lead_identity_hash, last_seen_at, estimated_budget_min, estimated_budget_max, lead_summary, contact_name, contact_email, contact_phone, company_name, status, price, max_buyers, buyers_count, created_at, updated_at.

Generacion automatica

  • Se crea lead marketplace por form_submit
  • Tambien cuando intent_score >= 12
  • Tambien cuando la visita toca paginas clave (precios/contacto/servicios/descargas)
  • Tambien por evento file_download
  • Se publica una sola oportunidad por visit_id
  • Deduplicacion por lead_identity_hash (email+phone+company+domain)

Estados

  • available: visible para compra
  • reserved: compra en curso
  • sold: alcanzo maximo de compradores
  • expired: lead antiguo/inactivo
Flujo completo: evento web -> calculo de intent -> creacion de marketplace_lead anonimo -> listado para clientes suscritos -> compra -> desbloqueo de contacto completo.
Metodo Endpoint Uso
GET /marketplace/leads Lista oportunidades anonimizadas segun filtros
POST /marketplace/leads/{lead_id}/purchase Compra y desbloquea datos completos del lead
POST /marketplace/leads/{lead_id}/anonymize Anonimiza contacto del lead por solicitud de eliminacion (Bearer token owner)
curl "https://analytics.bitnodo.net/api/v1/marketplace/leads?category=servicios%20legales&city=Santiago&min_intent_score=12&limit=20" \
  -H "X-API-Key: TU_API_KEY"
{
  "items": [
    {
      "lead_id": "ld_9231abf0",
      "category": "servicios legales",
      "sub_category": "abogado laboral",
      "city": "Santiago",
      "intent_score": 16,
      "estimated_budget_min": 300000,
      "estimated_budget_max": 800000,
      "lead_summary": "Empresa busca abogado laboral en Santiago. Nivel de interes 16. Presupuesto estimado entre $300.000 y $800.000.",
      "price": 7000,
      "created_at": "2026-03-11 18:40:00"
    }
  ]
}
curl -X POST "https://analytics.bitnodo.net/api/v1/marketplace/leads/ld_9231abf0/purchase" \
  -H "X-API-Key: TU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"buyer_portal_id":"PORTAL_ID_COMPRADOR"}'
{
  "lead_id": "ld_9231abf0",
  "contact_name": "Rodrigo Perez",
  "contact_email": "rperez@empresa.cl",
  "contact_phone": "+56912345678",
  "company_name": "Comercial Andina SpA",
  "city": "Santiago"
}
Pricing base del lead: intent 10-12 = $3000, intent 13-15 = $5000, intent 16+ = $7000. El evento lead.marketplace.created se emite al publicar un nuevo lead.
Privacidad: el marketplace no expone nombre/email/telefono en listado. El desbloqueo completo ocurre solo despues de compra valida con API key.
curl -X POST "https://analytics.bitnodo.net/api/v1/marketplace/leads/ld_9231abf0/anonymize" \
  -H "Authorization: Bearer TU_ACCESS_TOKEN"

Leads recuperables por API

Cuando un visitante envia un formulario y este contiene email y/o telefono validos, la API guarda esos datos en una entidad separada de leads para facilitar consulta comercial y atribucion de conversiones.

Reglas de captura

  • Solo se capturan en el submit del formulario
  • No se inspeccionan contrasenas ni campos sensibles ajenos al lead
  • Solo se guardan email y telefono si son validos
  • Se relacionan con pagina, portal y evento de origen
  • Si el formulario no tiene id, el widget genera un identificador estable automaticamente

Consulta

GET /api/v1/leads?limit=50

curl "https://analytics.bitnodo.net/api/v1/leads?limit=50" \
  -H "X-API-Key: TU_API_KEY"
Filtro Parametro Ejemplo
Portal portal_id PORTAL_ID_UNICO
Email email cliente@empresa.com
Telefono phone +56912345678
Desde fecha date_from 2026-03-01
Hasta fecha date_to 2026-03-31
Limite limit 50
curl "https://analytics.bitnodo.net/api/v1/leads?limit=50&portal_id=PORTAL_ID_UNICO&email=cliente@empresa.com&phone=%2B569&date_from=2026-03-01&date_to=2026-03-31" \
  -H "X-API-Key: TU_API_KEY"
{
  "items": [
    {
      "id": 12,
      "portal_id": "PORTAL_ID_UNICO",
      "visit_event_id": 301,
      "email": "cliente@empresa.com",
      "phone": "+56912345678",
      "page_url": "https://www.empresa.com/contacto",
      "form_id": "frm_k29a81",
      "form_name": "contacto",
      "source_event_type": "form_submit",
      "metadata": {
        "action": "https://www.empresa.com/contacto",
        "method": "POST",
        "page_identifier": "page_contacto",
        "field_count": 4,
        "lead_fields": {
          "email_field": "email",
          "phone_field": "telefono"
        }
      },
      "created_at": "2026-03-11 12:00:00"
    }
  ]
}

Sandbox de pruebas

El sandbox replica el comportamiento de la API de produccion de forma identica en validaciones y respuestas, pero no escribe nada en la base de datos. Ideal para integrar y probar tu cliente antes de usar credenciales reales.

Base URL sandbox

https://analytics.bitnodo.net/sandbox/v1

Caracteristicas

  • No requiere API key real — cualquier valor funciona
  • Mismas validaciones que produccion (422 si falta campo)
  • Respuestas con IDs ficticios generados aleatoriamente
  • Todos los responses incluyen "mode": "sandbox"
Metodo Endpoint Descripcion
GET /sandbox/v1/health Estado del sandbox (siempre ok)
POST /sandbox/v1/track Simula ingesta de evento individual (valida payload)
POST /sandbox/v1/track/batch Simula ingesta batch de hasta 20 eventos
POST /sandbox/v1/auth/login Retorna tokens ficticios con cualquier email/password
GET /sandbox/v1/portals Lista de portales de ejemplo
GET /sandbox/v1/alerts Alertas de ejemplo con datos ficticios
GET /sandbox/v1/visits Visitas de ejemplo con intent scores
GET /sandbox/v1/stats/overview Metricas de overview con datos ficticios

1. Health check

curl "https://analytics.bitnodo.net/sandbox/v1/health"
{
  "status": "ok",
  "service": "bitnodo-analytics",
  "version": "v1",
  "mode": "sandbox",
  "timestamp": "2026-03-11T18:30:00+00:00"
}

2. Enviar evento de tracking

curl -s -X POST "https://analytics.bitnodo.net/sandbox/v1/track" \
  -H "Content-Type: application/json" \
  -d '{
    "companyId":  "cualquier-account-id",
    "portalId":   "cualquier-portal-id",
    "visitorId":  "vtr_test_001",
    "sessionId":  "ses_test_001",
    "eventType":  "pageview",
    "pageUrl":    "https://www.miempresa.com/home"
  }'
{
  "ok": true,
  "message": "Event captured (sandbox — nothing stored)",
  "mode": "sandbox",
  "queued_event_id": "a3f8d1e2b4c9...",
  "visit_id": "e5b7f2a1c4d8...",
  "intent_score": 7,
  "intent_level": "normal",
  "echo": {
    "companyId": "cualquier-account-id",
    "portalId":  "cualquier-portal-id",
    "eventType": "pageview",
    "pageUrl":   "https://www.miempresa.com/home"
  }
}

3. Batch de eventos

curl -s -X POST "https://analytics.bitnodo.net/sandbox/v1/track/batch" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "companyId": "cualquier-account-id",
        "portalId":  "cualquier-portal-id",
        "visitorId": "vtr_test_001",
        "sessionId": "ses_test_001",
        "eventType": "pageview",
        "pageUrl":   "https://www.miempresa.com/home"
      },
      {
        "companyId": "cualquier-account-id",
        "portalId":  "cualquier-portal-id",
        "visitorId": "vtr_test_001",
        "sessionId": "ses_test_001",
        "eventType": "scroll_depth",
        "pageUrl":   "https://www.miempresa.com/home",
        "eventData": {"milestone": 75}
      }
    ]
  }'
{
  "ok": true,
  "mode": "sandbox",
  "accepted": 2,
  "rejected": 0,
  "results": [
    {"ok": true, "event_index": 0, "queued_event_id": "c2a1b3d4..."},
    {"ok": true, "event_index": 1, "queued_event_id": "f5e6a7b8..."}
  ]
}

4. Login sandbox

curl -s -X POST "https://analytics.bitnodo.net/sandbox/v1/auth/login" \
  -H "Content-Type: application/json" \
  -d '{"email":"test@ejemplo.com","password":"cualquierpassword"}'
{
  "mode": "sandbox",
  "user": {
    "id": "d4e5f6a7b8c9...",
    "email": "test@ejemplo.com",
    "full_name": "Sandbox User",
    "account_id": "a1b2c3d4e5f6...",
    "tenant_id":  "f6e5d4c3b2a1..."
  },
  "account": {
    "id":   "a1b2c3d4e5f6...",
    "name": "Sandbox Account",
    "plan": "pro",
    "api_key":    "bnk_sandbox_test_key",
    "public_key": "pk_sandbox_test",
    "secret_key": "sk_sandbox_test"
  },
  "access_token": "atk_sandbox_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

5. Leer alertas / visitas / stats (ejemplo)

curl "https://analytics.bitnodo.net/sandbox/v1/alerts"
curl "https://analytics.bitnodo.net/sandbox/v1/visits"
curl "https://analytics.bitnodo.net/sandbox/v1/stats/overview"
curl "https://analytics.bitnodo.net/sandbox/v1/portals"

Ejemplo completo en JavaScript (fetch)

// 1. Hacer login en sandbox
const loginRes = await fetch('https://analytics.bitnodo.net/sandbox/v1/auth/login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'test@ejemplo.com', password: 'test123' })
});
const { access_token } = await loginRes.json();

// 2. Enviar evento
await fetch('https://analytics.bitnodo.net/sandbox/v1/track', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    companyId:  'cualquier-account-id',
    portalId:   'cualquier-portal-id',
    visitorId:  'vtr_test_001',
    sessionId:  'ses_test_001',
    eventType:  'pageview',
    pageUrl:    window.location.href
  })
});

// 3. Consultar overview
const statsRes = await fetch('https://analytics.bitnodo.net/sandbox/v1/stats/overview');
const stats = await statsRes.json();
console.log(stats); // { visits_today: 124, leads_today: 6, ... mode: "sandbox" }
El sandbox valida exactamente igual que produccion: si envias un payload incompleto (ej: sin companyId), el sandbox devuelve HTTP 422 con el mismo mensaje de error que producccion. Esto garantiza que tu integracion funcionara igual al pasar a produccion.

Salud de sistema y control de trafico

Politica de ingesta activa en /track y /track/batch: 100 req/min por IP y 2000 eventos/min por portal.

Health check publico

GET /health

{
  "status": "ok",
  "service": "bitnodo-analytics",
  "version": "v1",
  "timestamp": "2026-03-11T18:30:00Z"
}

Status operativo de portal

GET /portals/{id}/status (Bearer token)

{
  "portal_id": "PORTAL_ID",
  "widget_detected": true,
  "last_event_at": "2026-03-11 16:10:11",
  "events_last_24h": 1243,
  "visits_last_24h": 301
}

Lineamientos de Desarrollo y Compatibilidad

Bitnodo Analytics evoluciona con foco en estabilidad del contrato publico. La arquitectura interna puede cambiar para mejorar rendimiento, escalabilidad y seguridad, manteniendo compatibilidad con integraciones existentes.

Contrato publico estable

  • Autenticacion y gestion de portales.
  • POST /api/v1/track.
  • POST /api/v1/track/batch.
  • GET /api/v1/visits, /alerts, /stats/overview, /leads.
  • GET /api/v1/marketplace/leads.
  • GET /v1/widget/config/{tenant_id}.

Cambios internos permitidos

  • Nuevas tablas, indices y refactor de esquema.
  • Batching, caching, rate limiting y mejoras de pipeline.
  • Optimizaciones de session handling y scoring.
  • Ajustes internos del widget sin romper integracion publica.
Principio clave: ingesta desacoplada del procesamiento. Widget -> /track -> almacenamiento inicial -> workers internos -> agregacion y scoring.
Si una mejora requiere alterar respuesta JSON de endpoints publicos, se debe mantener retrocompatibilidad o versionar en una ruta nueva (por ejemplo /v2/*).

Errores HTTP comunes

401 Unauthorized

Token Bearer invalido/expirado o API key incorrecta.

404 Not Found

Endpoint no existe o id de recurso no valido.

409 Conflict

Intento de registro con email ya existente.

422 Unprocessable Entity

Payload invalido o faltan campos requeridos.

429 Too Many Requests

Rate limit superado en tracking o lecturas. Reintenta en la siguiente ventana.

Seguridad y cumplimiento

  • Passwords protegidas con Argon2id.
  • Tokens y API keys guardados hashados en base de datos.
  • Aislamiento por cuenta (multi-tenant).
  • Consultas SQL preparadas (PDO).
  • Aviso legal integrado en el widget.
  • Captura de datos de contacto solo al enviar formularios.
Se recomienda definir politicas de retencion, consent, tratamiento de leads y dominios CORS permitidos en produccion.