Envíe eventos de análisis directamente desde su backend a Zenovay. El seguimiento del lado del servidor es inmune a los bloqueadores de anuncios, le permite registrar eventos que solo ocurren en el servidor (compras confirmadas, suscripciones impulsadas por webhook) y le da control total sobre lo que se envía.
¿Por qué el seguimiento del lado del servidor?
Beneficios
| Beneficio | Descripción |
|---|---|
| Inmunidad contra bloqueadores de anuncios | Los eventos llegan a Zenovay incluso cuando el script del cliente está bloqueado por el navegador |
| Eventos solo del servidor | Rastree cosas que ocurren solo del lado del servidor — pagos confirmados, trabajos cron, webhooks |
| Control de datos | Usted decide exactamente qué se envía |
| Seguimiento híbrido | Combine con el script del cliente para una cobertura completa |
Cuándo usar
- Confirmación de compra en comercio electrónico (después de la confirmación del proveedor de pago)
- Eventos de backend (suscripción creada, suscripción cancelada)
- Eventos impulsados por webhook (Stripe, proveedores de pago)
- Seguimiento híbrido (script del cliente para navegación + servidor para conversiones confirmadas)
Endpoint de seguimiento
Las solicitudes del lado del servidor utilizan el mismo endpoint de ingesta que el script del cliente. Envíe un POST al endpoint de seguimiento con el código de seguimiento de su sitio en la ruta:
POST https://api.zenovay.com/e/{trackingCode}
Este es un endpoint público, no autenticado — no requiere una clave API y está abierto en todos los planes (es la misma ruta a la que el script de seguimiento envía). El código de seguimiento es el mismo valor que pone en el atributo data-tracking-code de su snippet de instalación.
Información
El endpoint de ingesta se comparte con el rastreador del navegador, por lo que su carga útil está estructurada como los datos que enviaría un navegador. Esto significa que algunos campos que el navegador completa automáticamente — session_id, device_type, browser, os y user_agent — deben ser suministrados por usted en el cuerpo JSON cuando se llama desde un servidor. Las solicitudes a las que les faltan estos campos se rechazan.
Campos requeridos
Cada evento que envíe debe incluir estos campos en el cuerpo JSON:
| Campo | Notas |
|---|---|
session_id | Una cadena de al menos 8 caracteres. Reutilice el mismo valor para eventos que pertenecen a la misma visita. |
url | Una URL completa y válida (por ejemplo https://example.com/checkout/success). |
device_type | por ejemplo desktop, mobile, tablet o server. |
browser | Un nombre de navegador/cliente. Use algo descriptivo para tráfico de servidor (por ejemplo server). |
os | Un nombre de sistema operativo (por ejemplo Linux). |
user_agent | Una cadena de agente de usuario. Evite agentes genéricos parecidos a bots (curl, python, wget, etc.) — se filtran como bots. |
visitor_id es opcional pero recomendado (≥8 caracteres) para que los eventos repetidos se vinculen al mismo visitante.
Vista de página vs. Evento personalizado
El tipo de evento se establece con el campo event_type:
- Vista de página — omita
event_type(predeterminado) o establézcalo enpageview. - Evento personalizado — establezca
event_typeencustom, coloque el nombre del evento enevent_namey cualquier metadato enproperties.
# Evento personalizado (por ejemplo, una compra confirmada)
curl -X POST "https://api.zenovay.com/e/YOUR_TRACKING_CODE" \
-H "Content-Type: application/json" \
-d '{
"event_type": "custom",
"event_name": "purchase",
"url": "https://example.com/checkout/success",
"referrer": "https://example.com/cart",
"session_id": "srv-9f2a7c41bd",
"device_type": "server",
"browser": "server",
"os": "Linux",
"user_agent": "MyApp-Server/1.0",
"properties": {
"order_id": "ORD-12345",
"value": 99.99,
"currency": "USD"
}
}'
# Vista de página
curl -X POST "https://api.zenovay.com/e/YOUR_TRACKING_CODE" \
-H "Content-Type: application/json" \
-d '{
"event_type": "pageview",
"url": "https://example.com/products/widget",
"referrer": "https://google.com/search",
"session_id": "srv-9f2a7c41bd",
"device_type": "server",
"browser": "server",
"os": "Linux",
"user_agent": "MyApp-Server/1.0"
}'
Una nota sobre la geolocalización
Para el tráfico del navegador, Zenovay obtiene la ubicación de la IP del visitante que se conecta. Cuando llama al endpoint desde su servidor, la IP que se conecta es la de su servidor — por lo que los eventos se geolocalizarán en su infraestructura, no en el usuario final.
Si necesita geolocalización precisa por visitante, realice ese seguimiento del lado del cliente (el snippet de instalación estándar) o use el proxy de primer nivel, y reserve el seguimiento del lado del servidor para eventos de backend (compras, suscripciones) donde la ubicación exacta del usuario no es el punto. Agregar un encabezado X-Forwarded-For en una llamada simple de servidor no anula la IP del servidor para el endpoint público.
Ejemplos de implementación
Node.js / Express
// lib/analytics.js
const TRACKING_CODE = process.env.ZENOVAY_TRACKING_CODE;
const ENDPOINT = `https://api.zenovay.com/e/${TRACKING_CODE}`;
// Campos base que cada evento del lado del servidor necesita.
const SERVER_DEFAULTS = {
device_type: 'server',
browser: 'server',
os: 'Linux',
user_agent: 'MyApp-Server/1.0'
};
async function trackEvent(eventName, { url, sessionId, properties = {} }) {
const payload = {
event_type: 'custom',
event_name: eventName,
url,
session_id: sessionId,
properties,
...SERVER_DEFAULTS
};
try {
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
return response.ok;
} catch (error) {
console.error('Analytics error:', error);
return false;
}
}
async function trackPageview({ url, sessionId, referrer = '' }) {
const payload = {
event_type: 'pageview',
url,
referrer,
session_id: sessionId,
...SERVER_DEFAULTS
};
try {
const response = await fetch(ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
return response.ok;
} catch (error) {
console.error('Pageview tracking error:', error);
return false;
}
}
module.exports = { trackEvent, trackPageview };
Python (Flask)
# analytics.py
import os
import requests
from flask import request
TRACKING_CODE = os.getenv('ZENOVAY_TRACKING_CODE')
TRACKING_URL = f'https://api.zenovay.com/e/{TRACKING_CODE}'
SERVER_DEFAULTS = {
'device_type': 'server',
'browser': 'server',
'os': 'Linux',
'user_agent': 'MyApp-Server/1.0',
}
def track_event(event_name, session_id, url, properties=None):
payload = {
'event_type': 'custom',
'event_name': event_name,
'url': url,
'session_id': session_id,
'properties': properties or {},
**SERVER_DEFAULTS,
}
try:
response = requests.post(TRACKING_URL, json=payload, timeout=5)
return response.ok
except Exception as e:
print(f'Analytics error: {e}')
return False
def track_pageview(session_id, url, referrer=''):
payload = {
'event_type': 'pageview',
'url': url,
'referrer': referrer,
'session_id': session_id,
**SERVER_DEFAULTS,
}
try:
response = requests.post(TRACKING_URL, json=payload, timeout=5)
return response.ok
except Exception as e:
print(f'Pageview tracking error: {e}')
return False
PHP
<?php
class ZenovayAnalytics {
private $trackingUrl;
private $defaults = [
'device_type' => 'server',
'browser' => 'server',
'os' => 'Linux',
'user_agent' => 'MyApp-Server/1.0',
];
public function __construct($trackingCode) {
$this->trackingUrl = "https://api.zenovay.com/e/{$trackingCode}";
}
public function trackEvent($eventName, $sessionId, $url, $properties = []) {
$payload = array_merge([
'event_type' => 'custom',
'event_name' => $eventName,
'url' => $url,
'session_id' => $sessionId,
'properties' => $properties,
], $this->defaults);
return $this->sendRequest($payload);
}
public function trackPageview($sessionId, $url, $referrer = '') {
$payload = array_merge([
'event_type' => 'pageview',
'url' => $url,
'referrer' => $referrer,
'session_id' => $sessionId,
], $this->defaults);
return $this->sendRequest($payload);
}
private function sendRequest($payload) {
$ch = curl_init($this->trackingUrl);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5
]);
curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $httpCode >= 200 && $httpCode < 300;
}
}
// Uso
$analytics = new ZenovayAnalytics(getenv('ZENOVAY_TRACKING_CODE'));
$analytics->trackEvent('purchase', 'srv-9f2a7c41bd', 'https://example.com/checkout/success', [
'order_id' => 'ORD-12345',
'value' => 99.99
]);
Seguimiento híbrido
El patrón más común: rastree la navegación con el script del cliente y confirme conversiones desde el servidor una vez que el proveedor de pago las ha confirmado.
// Del lado del cliente: rastree la intención usando el script de Zenovay
window.zenovay('track', 'checkout_started');
// Del lado del servidor: rastree el evento confirmado desde su controlador de webhook
app.post('/webhooks/stripe', async (req, res) => {
const event = req.body;
if (event.type === 'checkout.session.completed') {
await trackEvent('purchase', {
url: 'https://example.com/checkout/success',
sessionId: 'srv-' + event.data.object.id.slice(-12),
properties: {
order_id: event.data.object.id,
value: event.data.object.amount_total / 100
}
});
}
res.sendStatus(200);
});
Filtrado de bots
Zenovay ya filtra tráfico de bot obvio en el lado de ingesta, por lo que los eventos enviados con agentes genéricos como curl, wget, python o cualquier cosa que coincida con patrones de crawler comunes se rechazan. Por eso los ejemplos anteriores usan un user_agent descriptivo para su servidor.
Si también reenvía solicitudes de cliente reales a través de su backend, filtre los crawlers antes de enviar para no quemar su límite de tasa en tráfico que de todas formas sería rechazado:
function isBot(userAgent) {
const botPatterns = [
/bot/i, /crawler/i, /spider/i, /scraper/i,
/curl/i, /wget/i, /python/i, /java\//i,
/googlebot/i, /bingbot/i, /yandex/i
];
return botPatterns.some(pattern => pattern.test(userAgent || ''));
}
Límites de velocidad
El endpoint de seguimiento tiene límites de velocidad por dirección IP:
- Límite de ráfaga: 60 solicitudes por 10 segundos
- Límite sostenido: 5.000 solicitudes por hora
Estos límites se aplican específicamente al endpoint de ingesta de seguimiento, no a la API REST (que es una característica pagada separada con sus propios límites).
Solución de problemas
Los eventos no aparecen
Verifique:
- El código de seguimiento en la ruta URL es correcto.
- Todos los campos requeridos están presentes (
session_idde 8+ caracteres,urlválida,device_type,browser,os,user_agent). Los campos faltantes devuelven un400con una lista de lo que falta. - Su
user_agentno se está filtrando como un bot (evitecurl,python, etc.). - La IP no tiene límite de velocidad.
Eventos duplicados
Asegúrese:
- No está rastreando el mismo evento tanto del lado del cliente como del servidor.
- Los controladores de webhook no se disparan más de una vez para el mismo evento.
La geolocalización refleja su servidor
Esto es esperado para las llamadas del lado del servidor — la IP que se conecta es su servidor, no el visitante. Use el script del cliente (o un proxy de primer nivel) para geolocalización precisa del visitante y use el seguimiento del lado del servidor para eventos de backend donde la ubicación no es el objetivo.