Saltar al contenido

Referencia de la API

Acceso programático a los leads, conversaciones, citas y conversiones que generan los agentes de Kelpi — con la atribución de campaña intacta desde el primer clic hasta el cierre.

Prefijo /v1Formato JSON · UTF-8Revisión 13 ago 2026

Autenticación

Todas las peticiones usan una API key por organización en el encabezado Authorization. Las llaves son de servidor: nunca las expongas en el navegador ni en una app móvil.

GET /v1/leads
Authorization: Bearer kelpi_sk_live_a7f3…

La llave queda ligada a una sola organización, así que todos los recursos que devuelve la API ya vienen acotados a tus datos — no hay que pasar un organizationId en ninguna llamada. Cada llave se puede revocar de forma independiente desde el panel, y registra su último uso.

Provisión de credenciales. Las llaves se emiten por integración y bajo solicitud, junto con el host de la API y un entorno de sandbox sembrado con datos de prueba. Los ejemplos de este documento usan rutas relativas al prefijo /v1.

Convenciones

Envolvente de respuesta

Toda respuesta exitosa devuelve la carga bajo data. Las colecciones agregan meta con el total para paginar.

{
  "data": [ … ],
  "meta": { "total": 1284, "limit": 20, "offset": 0 }
}

Paginación

Por limit (máx. 100, default 20) y offset. Para exportaciones completas, ordena por createdAt asc y recorre con offset hasta agotar meta.total.

Fechas

Todas las marcas de tiempo son ISO 8601 en UTC (2026-08-12T23:01:19.000Z). Los filtros de rango aceptan tanto YYYY-MM-DD como ISO completo.

Límite de tasa

100 peticiones por minuto por llave. Cada respuesta incluye X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset. Al excederlo se devuelve 429 con código rate_limited.

Trazabilidad de campaña en WhatsApp

Campaña en Google o Meta → landing → el usuario toca “WhatsApp” → la conversación arranca sin referrer y la campaña se pierde.

WhatsApp no propaga document.referrer ni los parámetros de la URL cuando se abre desde un wa.me. Kelpi lo resuelve trasladando la atribución al único canal que sí sobrevive el salto: el texto del primer mensaje.

  1. 1El píxel de Kelpi vive en la landing y captura la sesión de clic: utm_*, gclid, fbclid, referrer, URL de aterrizaje y user agent.
  2. 2Al tocar el botón de WhatsApp, el píxel acuña un token de referencia corto y lo anexa al mensaje prellenado: Hola, quiero informes · #K7F2M9.
  3. 3El mensaje llega a Kelpi. El pipeline de entrada extrae y descarta el token antes de que el agente lo lea — el prospecto nunca ve una conversación contaminada.
  4. 4El token se resuelve contra la sesión de clic y la atribución completa se estampa en el lead, en el objeto attribution.
  5. 5A partir de ahí, toda conversación, cita, handoff y conversión de ese lead hereda la campaña. Es lo que alimenta /v1/analytics/campaigns.

La ventana de atribución es configurable por organización — 30 días por defecto — y el modelo puede fijarse en last touch o first touch.

Anuncios de clic a WhatsApp de Meta

Cuando el tráfico viene de un anuncio Click-to-WhatsApp, no hace falta el token: Meta entrega el ctwa_clid en el webhook de la conversación y Kelpi lo resuelve directamente contra la campaña y el anuncio. Ambas rutas aterrizan en el mismo objeto attribution.

Píxel y conversiones de servidor

Instalación

Una etiqueta en todas las páginas del sitio. No requiere cookies de terceros.

<script src="{host}/api/embed/klpi.js"
        data-token="pk_live_9c21…" async></script>

El valor de {host} se entrega junto con las credenciales.

Botón de WhatsApp con atribución

Reemplaza el href de wa.me por esta llamada. Devuelve la URL ya firmada con el token de referencia.

// Enlaza el CTA con la sesión de clic actual
klpi('whatsapp', {
  phone:   '5218112345678',
  message: 'Hola, quiero informes de Torres Valle Oriente'
});

Eventos de embudo

El píxel acepta eventos de bajo riesgo desde el navegador. Los eventos sensibles — checkout_started, payment_method_added, purchase_succeeded — deben enviarse servidor a servidor y firmados, para que el monto no sea manipulable desde el cliente.

POST/conversions/webhook/{token}

Registra una conversión de servidor. Requiere X-Kelpi-Signature (HMAC-SHA256 hex del cuerpo crudo). Kelpi resuelve la atribución contra el timeline del lead y devuelve a qué campaña y actor quedó adjudicada.

Cuerpo

CampoTipoDescripción
event_typestringEvento del embudo. Ver catálogos.
lead_iduuidLead de Kelpi. Opcional si se manda phone o attribution_cookie.
phonestringE.164. Alternativa a lead_id para resolver el lead.
amountnumberMonto de la transacción.
currencystringISO 4217. Default MXN.
productstringEtiqueta libre del producto o unidad vendida.
external_transaction_idstringIdempotencia. Un mismo ID nunca registra dos conversiones.

Leads

El recurso central: los prospectos con su origen de campaña ya resuelto.

GET/v1/leads

Lista los leads de la organización con su atribución. Excluye automáticamente los leads fusionados como duplicados, de modo que los conteos no se inflan.

Parámetros de consulta

ParámetroTipoDescripción
searchstringCoincidencia parcial sobre nombre, correo y teléfono.
temperatureenumcold · warm · hot · ready
goalStatusenumpending · in_progress · achieved · failed
sourceTypeenumchannel · csv · manual · api · form · embed
utmCampaignstringFiltra por campaña atribuida.
utmSourcestringFiltra por fuente — google, meta, tiktok
utmMediumstringFiltra por medio — cpc, organic
adIdstringFiltra al nivel de anuncio individual.
createdFromdateLímite inferior de createdAt, inclusivo.
createdTodateLímite superior de createdAt, inclusivo.
updatedSincedatePara sincronización incremental — solo lo que cambió.
sortenumcreatedAt · updatedAt · name. Default createdAt.
orderenumasc · desc. Default desc.
limitint1–100. Default 20.
offsetintDefault 0.

Respuesta

{
  "data": [
    {
      "id": "9f2c1a7e-4b13-4d92-8a0f-1c73e5b8d204",
      "name": "Ana Ramírez",
      "phone": "+528112345678",
      "email": "ana.ramirez@example.mx",
      "temperature": "hot",
      "goalStatus": "in_progress",
      "sourceType": "channel",
      "contactAttempts": 2,
      "lastContactedAt": "2026-08-12T18:44:07.000Z",
      "isOptedOut": false,
      "attribution": {
        "utmSource":   "google",
        "utmMedium":   "cpc",
        "utmCampaign": "torres-valle-oriente-q3",
        "utmContent":  "carrusel-3rec",
        "utmTerm":     "departamentos san pedro",
        "gclid":       "Cj0KCQjw8vqrBhD…",
        "fbclid":      null,
        "ctwaClid":    null,
        "campaignId":  "20841773994",
        "adId":        "697412330051",
        "landingUrl":  "https://tu-sitio.com/preventa?utm_source=google&…",
        "referrer":    "https://www.google.com/",
        "resolvedVia": "whatsapp_ref_token",
        "firstTouchAt": "2026-08-11T16:02:55.000Z",
        "lastTouchAt":  "2026-08-12T18:44:07.000Z"
      },
      "createdAt": "2026-08-11T16:03:12.000Z",
      "updatedAt": "2026-08-12T18:44:07.000Z"
    }
  ],
  "meta": { "total": 1284, "limit": 20, "offset": 0 }
}

resolvedVia indica cómo se recuperó la atribución: whatsapp_ref_token, meta_ctwa, pixel_session, api_source_meta o unattributed. Sirve para auditar qué porcentaje del tráfico estás recuperando.

GET/v1/leads/{id}

Un lead con su atribución, sus campos personalizados y los contadores de conversaciones y citas.

POST/v1/leads

Crea un lead. Úsalo cuando la captura ocurre en un formulario propio y quieres que Kelpi lo trabaje, pasando la atribución que ya traes del lado de tu landing.

Cuerpo

CampoTipoDescripción
namestringRequerido.
phonestringE.164. Requerido para que el agente pueda contactarlo.
emailstringOpcional.
temperatureenumDefault cold.
attributionobjectMismos campos que devuelve el GET. Todos opcionales.
customFieldsobjectPares clave-valor propios.
initiateConversationboolSi es true, el agente abre la conversación de WhatsApp de inmediato.
messagestringMensaje inicial cuando initiateConversation está activo.
PATCH/v1/leads/{id}

Actualiza name, phone, email, temperature, goalStatus, isOptedOut o customFields. Dispara lead.updated.

Timeline de interacciones

GET/v1/leads/{id}/touches

Historial ordenado de cada punto de contacto del lead. Es la materia prima de cualquier modelo de atribución propio: con esto puedes reimplementar first-touch, last-touch, lineal o en U del lado de tu dashboard sin depender del nuestro.

{
  "data": [
    { "touchType": "link_clicked",          "occurredAt": "2026-08-11T16:02:55.000Z",
      "actorType": "campaign",           "metadata": { "utmCampaign": "torres-valle-oriente-q3" } },
    { "touchType": "whatsapp_sent",         "occurredAt": "2026-08-11T16:03:20.000Z",
      "actorType": "agent",              "actorId": "a1c8…" },
    { "touchType": "outbound_call",         "occurredAt": "2026-08-12T18:31:02.000Z",
      "actorType": "agent",              "relatedCallId": "c93f…" },
    { "touchType": "appointment_scheduled", "occurredAt": "2026-08-12T18:44:07.000Z",
      "actorType": "agent" }
  ]
}

Conversaciones

GET/v1/conversations

Conversaciones de WhatsApp y llamadas de voz en un mismo recurso — Kelpi las mantiene unificadas por lead. Filtros: status, outcome, channel, leadId, agentId, startedFrom, startedTo, utmCampaign.

CampoTipoDescripción
channelenumwhatsapp · call · web_embed
statusenumactive · paused · handed_off · closed
outcomeenumpending · successful · failed · no_response · opted_out
outcomeNotesstringResumen que el agente escribe al cerrar.
durationSecondsintSolo en llamadas.
messageCountintMensajes intercambiados.
recordingUrlstringAudio de la llamada. URL firmada, vigencia 24 h.
leadobjectid, name, phone, email.
agentobjectid, name.
GET/v1/conversations/{id}

La conversación con sus mensajes y, en llamadas, la transcripción completa con marcas de tiempo y el resumen de análisis del agente.

Citas

GET/v1/appointments

Citas agendadas por los agentes. Filtros: status, leadId, agentId, scheduledFrom, scheduledTo, utmCampaign.

CampoTipoDescripción
scheduledAtdatetimeInicio de la cita.
durationMinutesintDuración.
statusenumscheduled · confirmed · completed · cancelled · no_show
assignedToobjectMiembro del equipo dueño de la agenda.
notesstringContexto que el agente extrajo de la conversación.
sourceConversationIduuidConversación que la originó.

Conversiones

GET/v1/conversions

Transacciones registradas vía píxel o servidor, ya adjudicadas a una campaña. Filtros: from, to, utmCampaign, leadId.

CampoTipoDescripción
amountdecimalMonto.
currencystringISO 4217.
productInfoobjectDetalle libre del producto.
attributedViaenumlast_touch · first_touch · manual · unattributed
isWithinWindowboolSi cayó dentro de la ventana de atribución.
convertedAtdatetimeMomento de la transacción.

Analítica agregada

Los KPIs ya calculados, para no reconstruirlos a mano desde los recursos crudos.

GET/v1/analytics/campaigns

El embudo completo agrupado por campaña. Una sola llamada alimenta el tablero: de conversaciones iniciadas hasta ingreso atribuido.

Parámetros

ParámetroTipoDescripción
fromdateRequerido.
todateRequerido.
groupByenumutmCampaign · utmSource · adId · agentId. Default utmCampaign.
attributionModelenumlast_touch · first_touch. Default el de la organización.
{
  "data": [
    {
      "key":                   "torres-valle-oriente-q3",
      "utmSource":             "google",
      "leads":                 214,
      "conversationsStarted":  198,
      "conversationsEngaged":  143,
      "qualifiedLeads":        76,
      "appointmentsScheduled": 41,
      "appointmentsCompleted": 28,
      "handoffs":              12,
      "conversions":           9,
      "revenue":               2340000.00,
      "currency":              "MXN",
      "unattributedLeads":     7
    }
  ],
  "meta": { "from": "2026-07-01", "to": "2026-07-31", "attributionModel": "last_touch" }
}

qualifiedLeads cuenta los leads que el agente clasificó en hot o ready. unattributedLeads es el tráfico que no se pudo adjudicar — tu métrica directa de qué tanto dark traffic queda sin recuperar.

GET/v1/analytics/summary

Totales del periodo con comparación contra el periodo inmediato anterior: leads, conversaciones, tasa de respuesta, citas, handoffs, conversiones e ingreso.

Agentes

GET/v1/agents

Agentes configurados, su objetivo y sus canales. Útil para etiquetar métricas por agente en el tablero.

CampoTipoDescripción
namestringNombre del agente.
goalTypeenumObjetivo — agendar cita, calificar, informar, recuperar.
goalDescriptionstringObjetivo en lenguaje natural.
channelsarraywhatsapp · call
timezonestringZona horaria operativa.
isActiveboolSi está atendiendo.

Webhooks

Kelpi notifica en tiempo real hacia el endpoint que registres. Es la vía recomendada para mantener tu dashboard al día sin sondear la API.

POST/v1/webhooks

Registra una suscripción. La respuesta incluye el secret de firma — se devuelve una sola vez, al crearla.

{
  "url": "https://tu-servidor.com/webhooks/kelpi",
  "events": ["lead.created", "lead.qualified", "conversation.ended", "conversion.registered"]
}
GET/v1/webhooks

Lista las suscripciones activas. DELETE /v1/webhooks/{id} da de baja una.

Catálogo de eventos

EventoSe dispara cuando
lead.createdEntra un lead nuevo por cualquier canal — WhatsApp, anuncio de Meta, formulario, API o carga masiva. Incluye la atribución ya resuelta.
lead.updatedCambia cualquier campo del lead, incluidos temperature y goalStatus.
lead.qualifiedEl agente clasifica al lead como hot o ready. El disparo de MQL.
conversation.startedArranca una conversación de WhatsApp o una llamada.
conversation.messageCada mensaje entrante o saliente. Alto volumen — suscríbete solo si necesitas el detalle.
conversation.endedLa conversación cierra, con su outcome y notas.
call.completedTermina una llamada. Trae duración, grabación, transcripción y si se cumplió el objetivo.
appointment.createdSe agenda una cita.
appointment.rescheduledCambia de fecha u hora. Incluye el horario anterior.
appointment.cancelledSe cancela, con el motivo si se capturó.
handoff.createdEl agente transfiere a un humano — por petición del prospecto o por intención de compra detectada.
conversion.registeredSe registra una conversión y queda adjudicada a una campaña.

Forma del envío

Todos los eventos llegan como POST con el mismo sobre. Tiempo de espera de 10 segundos; responde 2xx rápido y procesa en segundo plano.

POST /webhooks/kelpi
Content-Type: application/json
X-Kelpi-Event: lead.qualified
X-Kelpi-Signature: 4f3ab9c1e07d…

{
  "event": "lead.qualified",
  "timestamp": "2026-08-12T18:44:07.000Z",
  "organizationId": "3b7f…",
  "data": { /* el recurso completo */ }
}

Verificar la firma

X-Kelpi-Signature es el HMAC-SHA256 en hexadecimal del cuerpo crudo, con el secret de la suscripción. Compara en tiempo constante.

const { createHmac, timingSafeEqual } = require('crypto');

function verify(rawBody, signature, secret) {
  const expected = createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  const a = Buffer.from(expected, 'hex');
  const b = Buffer.from(signature, 'hex');

  return a.length === b.length && timingSafeEqual(a, b);
}
Usa el cuerpo sin parsear. Si el framework deserializa el JSON antes de que calcules el HMAC, el reordenamiento de llaves rompe la firma. En Express: express.raw({ type: 'application/json' }) en esa ruta.

Catálogos

Temperatura del lead

cold · warm · hot · ready

Estatus de objetivo

pending · in_progress · achieved · failed

Origen del lead

channel · csv · manual · api · form · embed

Estatus de conversación

active · paused · handed_off · closed

Resultado de conversación

pending · successful · failed · no_response · opted_out

Estatus de cita

scheduled · confirmed · completed · cancelled · no_show

Tipos de interacción del timeline

inbound_call · outbound_call · link_sent · link_clicked · sms_sent · whatsapp_sent · appointment_scheduled · objection_handled · opt_out · manual_entry · form_opened · form_engaged · checkout_started · payment_method_added · purchase_succeeded · purchase_failed

Errores y límites

Los errores devuelven el código HTTP correspondiente y un cuerpo uniforme.

{
  "error": {
    "code": "validation_error",
    "message": "Invalid query parameters",
    "details": [ { "path": ["limit"], "message": "Number must be less than or equal to 100" } ]
  }
}
HTTPCódigoSignificado
400validation_errorParámetros o cuerpo inválidos. details señala el campo exacto.
401unauthorizedLlave ausente, mal formada o revocada.
404not_foundEl recurso no existe o no pertenece a la organización de la llave.
409conflictChoque de unicidad — por ejemplo un teléfono ya registrado.
429rate_limitedSe excedió el límite. Reintenta después de X-RateLimit-Reset.
500internal_errorError del lado de Kelpi. Reintentable.