Conecta KUZ CRM con lo que ya usas.
Una API REST en JSON para leer y escribir leads, clientes y tareas, y webhooks firmados para enterarte al instante cuando entra un lead, se gana una venta o llega un mensaje de WhatsApp.
Base: https://kuzcrm.com/api/v1JSON · UTF-8TLS obligatorioEspecificación OpenAPI 3.1
- Entra a Cuenta › API y webhooks y crea un token con los permisos que necesites.
- Prueba que funciona:
curl https://kuzcrm.com/api/v1/me \ -H "Authorization: Bearer kuz_live_XXXXXXXX…"
{
"organizacion": { "id": 12, "nombre": "Comercial Andes SpA", "plan": "pyme", "estado": "activa" },
"token": { "nombre": "Integración ERP", "scopes": ["leads:read", "leads:write"], "expiraEn": null },
"limites": { "solicitudes": "600 cada 15 minutos" }
}
Autenticación
Cada solicitud lleva un token de organización en la cabecera Authorization. Los tokens empiezan con kuz_live_, se crean desde el CRM (solo administradores) y se muestran una sola vez: guardamos únicamente su huella SHA-256.
Authorization: Bearer kuz_live_a1B2c3D4…
Permisos (scopes)
| Scope | Permite |
|---|---|
| leads:read | Leer leads y las etapas del pipeline |
| leads:write | Crear y modificar leads, mover de etapa |
| clientes:read | Leer clientes, salud, segmento, calificación de pagador y semáforo de riesgo |
| clientes:write | Crear y modificar clientes |
| tareas:read / tareas:write | Leer y crear tareas |
| eventos:write | Registrar actividades en la línea de tiempo de un cliente o lead |
| webhooks:manage | Crear y eliminar webhooks (lo usan Zapier y Make) |
Límites
| Cuenta | Límite por token |
|---|---|
| Plan activo (Emprende, PyME, Corporate) | 600 solicitudes cada 15 minutos |
| Prueba gratis de 14 días | 100 solicitudes por día |
Cada respuesta incluye X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset (unix). Al superar el límite recibes 429 con Retry-After. Los límites de tu plan (contactos, usuarios) también aplican: crear un lead o cliente cuando no queda cupo devuelve 402 LIMITE_PLAN. Una cuenta en solo lectura (prueba vencida o pago pendiente) acepta GET y rechaza escrituras con 402.
Paginación
Los listados se paginan por cursor. Pide hasta 100 por página y sigue next_cursor mientras has_more sea true. Los resultados vienen del más reciente al más antiguo.
GET /api/v1/leads?limit=100
GET /api/v1/leads?limit=100&cursor=4821 ← next_cursor de la página anterior
{ "data": [ … ], "next_cursor": 4721, "has_more": true }
Para sincronizar cambios usa actualizado_desde=2026-09-01T00:00:00Z y guarda la marca de tiempo de tu última corrida.
Errores
Todos los errores tienen la misma forma: un mensaje legible en español y un código estable para programar contra él.
{ "error": "Este token no tiene el permiso leads:write", "code": "SCOPE_INSUFICIENTE" }
| HTTP | Códigos | Qué hacer |
|---|---|---|
| 400 | VALIDACION, RUT_INVALIDO, ETAPA_INEXISTENTE, CLIENTE_INEXISTENTE, USUARIO_INEXISTENTE, URL_RECHAZADA, JSON_INVALIDO | Corrige el cuerpo; el mensaje indica el campo |
| 401 | SIN_TOKEN, TOKEN_INVALIDO, TOKEN_REVOCADO, TOKEN_EXPIRADO | Revisa la cabecera o crea un token nuevo |
| 402 | LIMITE_PLAN, PRUEBA_VENCIDA, SUSPENDIDA | Sube de plan o regulariza en Plan y facturación |
| 403 | SCOPE_INSUFICIENTE, ORG_CANCELADA | Añade el permiso al token desde el CRM |
| 404 | NO_ENCONTRADO, RUTA_INEXISTENTE | El recurso no existe o es de otra organización |
| 409 | RUT_DUPLICADO, DUPLICADO | Ya existe; la respuesta trae clienteId |
| 429 | RATE_LIMIT | Espera Retry-After segundos |
Idempotencia
Si tu integración puede reintentar un POST (corte de red, timeout), envía Idempotency-Key con un valor único por operación. Repetir la misma clave dentro de 24 horas devuelve la respuesta original con Idempotent-Replayed: true, sin crear duplicados.
curl -X POST https://kuzcrm.com/api/v1/leads \
-H "Authorization: Bearer $KUZ_TOKEN" -H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-88213" \
-d '{"nombre":"Juan Pérez","email":"juan@ejemplo.cl","valor":150000}'
Cuenta
/meOrganización, plan, permisos del token y límites vigentes. Útil para validar credenciales al configurar una integración.
Leads
Un lead es una oportunidad de venta en el pipeline. Campos principales: nombre (obligatorio), empresa, email, telefono, mensaje, fuente, etapa, valor (pesos), probabilidad (0-100), propietarioId, clienteId, cierreEstimado. KUZ calcula además score.
/leadsleads:readFiltros: etapa, fuente, email, telefono, q, desde, actualizado_desde, cliente_id.
curl "https://kuzcrm.com/api/v1/leads?etapa=nuevo&limit=20" -H "Authorization: Bearer $KUZ_TOKEN"
/leadsleads:writecurl -X POST https://kuzcrm.com/api/v1/leads \
-H "Authorization: Bearer $KUZ_TOKEN" -H "Content-Type: application/json" \
-d '{
"nombre": "Juan Pérez", "empresa": "Ejemplo Ltda",
"email": "juan@ejemplo.cl", "telefono": "+56912345678",
"valor": 150000, "fuente": "formulario", "externalId": "typeform-8812"
}'
/leads/{id}leads:readIncluye las últimas 50 actividades y tareas del lead.
/leads/{id}leads:writeEnvía solo los campos que cambian. Mover a una etapa de tipo ganada fija probabilidad en 100 y dispara lead.ganado.
curl -X PATCH https://kuzcrm.com/api/v1/leads/88 \
-H "Authorization: Bearer $KUZ_TOKEN" -H "Content-Type: application/json" \
-d '{"etapa":"ganado","valor":180000}'
Pipeline
/pipeline/etapasleads:readDevuelve las etapas de la organización en orden con su clave (la que usas en etapa) y su tipo: abierta, ganada o perdida.
Clientes
Un cliente es una empresa o persona con historial. Campos: razonSocial (obligatorio), rut (se valida el dígito verificador y se normaliza a 12.345.678-9), email, telefono, dirección (direccion, comuna, ciudad, region), giro, etiquetas, condicionPago, limiteCredito, propietarioId.
/clientesclientes:readFiltros: q, rut, email, segmento, etiqueta, con_deuda=1, desde, actualizado_desde.
/clientesclientes:writecurl -X POST https://kuzcrm.com/api/v1/clientes \
-H "Authorization: Bearer $KUZ_TOKEN" -H "Content-Type: application/json" \
-d '{"razonSocial":"Comercial Andes SpA","rut":"76.086.428-5","email":"pagos@andes.cl","condicionPago":"30 dias","etiquetas":["mayorista"]}'
/clientes/{id}clientes:readFicha completa con contactos, direcciones, métricas de compra (totalCompras, salud, segmento, saldoPendiente) y dos bloques de Finanzas:
pagadorComportamiento de pago calculado con los documentos y pagos registrados (ERP o manual):
calificacion (excelente · bueno · regular · malo · sin_datos), diasPromedioPago, diasPromedioAtraso, pctATiempo, dso y los motivos.riesgoComercialResultado de la última evaluación comercial de tu organización sobre ese RUT:
semaforo (verde · amarillo · rojo · gris), score, etiqueta y evaluadoEn. Nunca se exponen los datos de origen del informe./clientes/{id}clientes:writeTareas
/tareastareas:readFiltros: estado (pendiente, en_curso, hecha), asignado_id, lead_id, cliente_id.
/tareastareas:writecurl -X POST https://kuzcrm.com/api/v1/tareas \
-H "Authorization: Bearer $KUZ_TOKEN" -H "Content-Type: application/json" \
-d '{"titulo":"Llamar para confirmar pedido","prioridad":"alta","vence":"2026-09-10T15:00:00Z","clienteId":3}'
Si omites asignadoId, la tarea queda asignada al propietario de la cuenta.
Eventos (actividades)
/eventoseventos:writeDeja una entrada en la línea de tiempo de la ficha 360 del cliente o del lead: una factura emitida en tu ERP, una visita registrada en tu app de terreno, un correo enviado desde otra herramienta.
curl -X POST https://kuzcrm.com/api/v1/eventos \
-H "Authorization: Bearer $KUZ_TOKEN" -H "Content-Type: application/json" \
-d '{"clienteId":3,"tipo":"externo","detalle":"Factura 10233 emitida en el ERP por $1.190.000"}'
tipo: llamada, email, reunion, whatsapp, visita, nota o externo (por defecto).
Webhooks
Un webhook es una URL tuya a la que KUZ hace un POST cada vez que ocurre un evento al que te suscribiste. Se crean en Cuenta › API y webhooks o por API. Al crearlo recibes un secreto de firma que solo se muestra una vez.
X-Kuz-Event | Nombre del evento, p. ej. lead.creado |
X-Kuz-Delivery | Id único de la entrega (whd_1042). Guárdalo para descartar duplicados en reintentos. |
X-Kuz-Attempt | Número de intento (1 a 6) |
X-Kuz-Signature | t=<unix>,v1=<hex> · firma HMAC-SHA256 (ver abajo) |
POST /tu/webhook HTTP/1.1
Content-Type: application/json; charset=utf-8
X-Kuz-Event: lead.ganado
X-Kuz-Delivery: whd_1042
X-Kuz-Signature: t=1788494574,v1=5f2c9e…
{
"id": "evt_1042",
"evento": "lead.ganado",
"creadoEn": "2026-09-04T13:05:00.000Z",
"orgId": 12,
"data": { "id": 88, "nombre": "Juan Pérez", "empresa": "Ejemplo Ltda", "etapa": "ganado", "valor": 150000, "probabilidad": 100, … }
}
Reglas de entrega
- Responde 2xx en menos de 10 segundos. Procesa en segundo plano si necesitas más tiempo.
- Si falla, reintentamos a 1 min, 5 min, 30 min, 2 h y 12 h. Después la entrega se marca abandonada (puedes reintentarla a mano desde el CRM).
- Tras 20 fallos seguidos el webhook se desactiva y avisamos por correo al propietario de la cuenta.
- Solo aceptamos URLs
https://públicas. Se rechazan IPs privadas, loopback, link-local y nombres internos; no seguimos redirecciones. - Las entregas pueden llegar desordenadas o repetidas: usa
X-Kuz-Deliverypara deduplicar y la fecha del recurso para ordenar.
Verificar la firma
Calcula HMAC_SHA256(secreto, t + "." + cuerpo_crudo) y compáralo con v1 en tiempo constante. Rechaza si t tiene más de 5 minutos. Usa el cuerpo crudo tal como llegó, sin re-serializar el JSON.
import express from "express";
import crypto from "node:crypto";
const app = express();
const SECRETO = process.env.KUZ_WEBHOOK_SECRET; // whsec_…
app.post("/webhooks/kuz", express.raw({ type: "application/json" }), (req, res) => {
const firma = Object.fromEntries((req.get("X-Kuz-Signature") || "").split(",").map((p) => p.split("=")));
const t = Number(firma.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return res.status(400).send("firma vieja");
const esperado = crypto.createHmac("sha256", SECRETO).update(`${t}.${req.body}`).digest("hex");
const ok = esperado.length === (firma.v1 || "").length && crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(firma.v1));
if (!ok) return res.status(401).send("firma inválida");
const evento = JSON.parse(req.body);
// Responde rápido y procesa después.
res.sendStatus(200);
procesar(evento).catch(console.error);
});
import hmac, hashlib, time, json
from flask import Flask, request, abort
app = Flask(__name__)
SECRETO = b"whsec_..."
@app.post("/webhooks/kuz")
def kuz():
partes = dict(p.split("=") for p in request.headers.get("X-Kuz-Signature", "").split(","))
t = int(partes.get("t", 0))
if abs(time.time() - t) > 300:
abort(400)
esperado = hmac.new(SECRETO, f"{t}.".encode() + request.get_data(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(esperado, partes.get("v1", "")):
abort(401)
evento = json.loads(request.get_data())
# encola y responde 200 de inmediato
return "", 200
<?php
$secreto = getenv('KUZ_WEBHOOK_SECRET');
$cuerpo = file_get_contents('php://input');
parse_str(str_replace(',', '&', $_SERVER['HTTP_X_KUZ_SIGNATURE'] ?? ''), $f);
if (abs(time() - (int)($f['t'] ?? 0)) > 300) { http_response_code(400); exit; }
$esperado = hash_hmac('sha256', ($f['t']) . '.' . $cuerpo, $secreto);
if (!hash_equals($esperado, $f['v1'] ?? '')) { http_response_code(401); exit; }
$evento = json_decode($cuerpo, true);
http_response_code(200);
Lista de eventos
| Evento | Cuándo se envía | data |
|---|---|---|
lead.creado | Entra un lead por formulario, chat web, WhatsApp, Messenger, Instagram, manual o API | Lead |
lead.etapa_cambiada | Un lead cambia de etapa | Lead + etapaAnterior |
lead.ganado / lead.perdido | Pasa a una etapa ganada o perdida | Lead |
cliente.creado / cliente.actualizado | Se crea o edita un cliente | Cliente |
tarea.creada / tarea.completada | Se crea una tarea o se marca hecha | Tarea |
conversacion.mensaje_entrante | Llega un mensaje por WhatsApp, Messenger o Instagram | conversacionId, canal, nombre, telefono, texto, leadId, clienteId |
ticket.creado | Se abre un ticket de SAC | numero, tipo, canal, asunto, prioridad, clienteId, slaVence |
evaluacion.completada | Termina una evaluación comercial | rut, razonSocial, semaforo, score, etiqueta, clienteId (solo el resultado) |
Suscríbete con "*" para recibir todos los eventos, incluidos los que agreguemos en el futuro.
Gestionarlos por API
/webhookswebhooks:manage/webhookswebhooks:managecurl -X POST https://kuzcrm.com/api/v1/webhooks \
-H "Authorization: Bearer $KUZ_TOKEN" -H "Content-Type: application/json" \
-d '{"url":"https://hooks.zapier.com/hooks/catch/123/abc","eventos":["lead.creado","lead.ganado"]}'
{ "id": 7, "url": "https://hooks.zapier.com/…", "eventos": ["lead.creado","lead.ganado"], "activo": true,
"secreto": "whsec_…", "aviso": "Guarda el secreto ahora: no se vuelve a mostrar." }
/webhooks/{id}webhooks:manageZapier
La app oficial de KUZ para Zapier está en revisión. Mientras se publica, todo funciona con los pasos nativos Webhooks by Zapier:
- Disparador (KUZ → Zapier): crea un Zap con «Webhooks by Zapier › Catch Hook», copia la URL y regístrala en KUZ como webhook con los eventos que quieras.
- Acción (Zapier → KUZ): «Webhooks by Zapier › Custom Request» con método
POST, URLhttps://kuzcrm.com/api/v1/leads, cabeceraAuthorization: Bearer <token>,Content-Type: application/jsony el cuerpo con los campos del lead.
Make
Usa HTTP › Make a request para llamar a la API (cabecera Authorization) y Webhooks › Custom webhook para recibir eventos. Para paginar, itera mientras has_more sea verdadero pasando next_cursor.