Ingesta de eventos
Enviar eventos de uso a LealUp — schema, batching, deduplicación y cómo alimentan el health score.
La ingesta de eventos es la forma como LealUp sabe qué están haciendo los usuarios en tu producto. Alimenta la dimensión Uso del producto del health score y activa playbooks basados en comportamiento.
Conceptos
- Evento: un hecho que pasó (ej. "user_logged_in", "report_exported", "feature_X_clicked").
- Customer: a qué cliente corresponde el evento (required). Lo identificás con tu ID interno (
external_customer_id) o con el UUID interno de LealUp (customer_id). - User (opcional): qué usuario específico dentro del cliente disparó el evento.
- Properties (opcional): metadata del evento (reporte tipo X, monto Y, etc.).
Endpoint
POST /v1/ingest/eventsAuth: header X-API-Key.
Body: array de eventos, hasta 100 por batch.
Schema de un evento
Recomendado — identificá al cliente con tu propio ID:
{
"external_customer_id": "acme_42", // tu ID interno del cliente
"external_source": "internal", // opcional, default "internal"
"event_name": "report_exported", // required, snake_case
"user_id": "u_12345", // opcional
"timestamp": "2026-04-15T14:25:00Z", // opcional, default = ahora
"properties": { // opcional
"report_type": "quarterly",
"format": "pdf",
"size_mb": 2.3
}
}Alternativa — si ya tenés el UUID de LealUp en tu sistema:
{
"customer_id": "01HCUS...", // UUID interno de LealUp
"event_name": "report_exported",
"timestamp": "2026-04-15T14:25:00Z"
}Tip: usá
external_customer_idcon tu propio ID (CRM, base de datos, lo que sea). LealUp resuelve internamente al cliente correcto. No necesitás guardar UUIDs nuestros en tu sistema.
Validaciones
- Referencia al cliente — exactamente uno entre
customer_idyexternal_customer_idpor evento. Si no envías ninguno →422 customer_reference_required. Si envías ambos, ganacustomer_id. external_customer_id— debe existir como cliente conexternal_idigual en tu workspace. Si no resuelve → error per-eventunknown_external_customer. La búsqueda es tenant-scoped: el ID de un workspace nunca resuelve en otro.external_source— string, default"internal". Útil si tenés clientes desde múltiples sistemas (ej."salesforce","stripe","hubspot"); cada uno mantiene su propio espacio de IDs.customer_id— UUID válido. Si no es UUID → error per-eventinvalid_customer_id. Si no existe en el workspace →unknown_customer.event_name— snake_case, max 255 chars.timestamp— ISO 8601 con timezone. Aceptamos hasta 30 días en el pasado; eventos más viejos se rechazan (usa endpoint de backfill separado).properties— JSON object, max 10KB por evento.
Batching
Envía batches, no uno por uno:
POST /v1/ingest/events
X-API-Key: sk_live_...
Content-Type: application/json
{
"events": [
{"external_customer_id": "acme_42", "event_name": "login", "timestamp": "..."},
{"external_customer_id": "acme_42", "event_name": "page_view", "timestamp": "..."},
...hasta 100
]
}Respuesta (202 Accepted):
{
"accepted": 98,
"rejected": 2,
"errors": [
{"index": 5, "event_name": "login", "code": "unknown_external_customer", "message": "external_customer_id 'foo' (source='internal') not found for this tenant"},
{"index": 42, "event_name": "report_exported", "code": "unknown_customer", "message": "customer_id '01HCUS...' not found for this tenant"}
],
"message": "Accepted 98 events; 2 rejected"
}Los eventos válidos se procesan; los rejected quedan listados con su código por evento (unknown_external_customer, unknown_customer, invalid_customer_id) para que los corrijas. Si ningún evento del batch tiene referencia al cliente, devolvemos 422 con código customer_reference_required.
Rate limits
- 60 batches/min por workspace (hasta 1000 eventos cada uno = 60k eventos/min en burst).
- 1M eventos/día en plan Starter, 10M en Growth, 100M en Scale, ilimitado en Enterprise.
- Excesos →
429 Too Many RequestsconRetry-After.
Deduplicación
Usa external_id para prevenir duplicados:
{
"customer_id": "01HCUS...",
"event_name": "purchase_completed",
"external_id": "order_12345", // tu ID interno
"timestamp": "...",
"properties": {"amount_usd": 99}
}Si reenviás el mismo external_id + customer_id, LealUp ignora el duplicado silenciosamente. Útil en reintentos con backoff.
Cómo alimentan el health score
Frecuencia de uso
- Días/semana con al menos un evento → dimensión Uso del producto.
- Una caída brusca vs baseline de 30 días → puede gatillar alerta.
Amplitud
- Número de eventos distintos (diversidad) → señal de adopción.
- Clientes que usan 2 features tienen menor amplitud que los que usan 10.
Usuarios activos
- DAU / MAU por cliente → señal de adopción horizontal (más usuarios activos = más sticky).
Eventos marcados como "aha moment"
Puedes marcar eventos específicos en Admin → Salud → Aha moments:
- Ej.
first_report_shared→ signifiant positive signal. - Ej.
billing_error_encountered→ negative signal.
Ejemplos prácticos
Ingesta server-side (Node.js)
import { LealUpClient } from "@lealup/sdk";
const client = new LealUpClient({ apiKey: process.env.LEALUP_KEY! });
// En tu backend, después de cualquier acción relevante.
// Usá tu propio ID de cliente — no necesitás guardar UUIDs de LealUp.
async function trackEvent(
yourCustomerId: string,
userId: string,
event: string,
properties?: Record<string, unknown>,
) {
await client.events.ingest({
events: [{
external_customer_id: yourCustomerId,
user_id: userId,
event_name: event,
timestamp: new Date().toISOString(),
properties,
}],
});
}Batching para volumen alto
Si generas >100 eventos/seg, no envíes uno por uno. Usa un buffer en memoria con flush periódico:
class EventBuffer {
private buffer: Event[] = [];
private flushInterval = 5_000; // 5 seg
constructor(private client: LealUpClient) {
setInterval(() => this.flush(), this.flushInterval);
}
add(event: Event) {
this.buffer.push(event);
if (this.buffer.length >= 500) this.flush(); // flush si llenamos
}
async flush() {
if (this.buffer.length === 0) return;
const batch = this.buffer.splice(0, 1000);
await this.client.events.ingest({ events: batch });
}
}Ingesta desde PostHog / Mixpanel
Si ya mandás eventos a otro product analytics, no los reingestes. Usa la integración nativa:
- PostHog → ver Otras integraciones.
- Mixpanel → en beta.
LealUp extrae los eventos relevantes (no todos) para no hinchar el score.
Backfill histórico
Para cargar eventos más antiguos que 30 días:
POST /v1/events/backfillMismo schema, acepta timestamps hasta 2 años atrás. Rate-limited más agresivamente, corre async. Útil para onboarding (cargar históricos una vez).
Eventos sistema (no los generas tú)
LealUp genera internamente algunos eventos que aparecen en el timeline pero no necesitas enviar:
email_sent/email_received— desde Gmail.meeting_held— desde Calendar.ticket_opened/ticket_closed— desde Zendesk/Jira.health_changed— cuando el score cruza umbral.task_created/task_completed— desde playbooks.
No los dupliques con ingesta manual.
Qué NO enviar
- Eventos ruidosos (
button_hovered,scroll). No aportan a health, saturan storage. - PII innecesaria — no mandes nombres, emails o direcciones en
propertiessalvo que los necesites. - Payload pesado — si
propertieses >10KB, lo rechazamos. Usa IDs y relaciona después.
Reglas de higiene
- Usa
snake_caseenevent_namey keys deproperties. - Mantén una lista de eventos válidos documentada en tu repo (taxonomía).
- No renombres eventos existentes — pierde historia. Si necesitas, crea uno nuevo (
report_exported_v2). - Test en dev antes de prod.
Debugging
"No veo mis eventos"
GET /v1/events?customer_id=X&from=2026-04-15— lista los eventos registrados.- Verifica
customer_id— errores silenciosos si el customer no existe en ese workspace. - Revisa Admin → Datos → Ingesta → Logs para ver rejections recientes.
"Health score no cambia"
- El score se recalcula cada hora por default (config en
Admin → Salud). - Si mandaste eventos recién, puede tardar hasta 1h. Usa
POST /v1/customers/{id}/health/recalculatepara forzar.
"Demasiados errores unknown_external_customer o customer_id not found"
- Los customers se crean via
POST /v1/customerso CSV import, antes de mandar eventos. - Si usás
external_customer_id, asegurate de que el cliente exista conexternal_idigual yexternal_sourceque coincida (default"internal"). - Si tu flow es "usuario se registra en tu app → mandar evento", asegurate de crear el customer primero (patrón provisioning).
Siguiente
- Webhooks — recibir señales salientes cuando pasan cosas en LealUp.