API y webhooks de kotiza
Conecta kotiza con tu CRM, tu ERP o Zapier: lee tus cotizaciones y clientes, crea clientes y recibe un aviso cuando una cotización se envía, se aprueba o se factura. Disponible en el plan Pro.
1. Autenticación
Un administrador crea la llave en Configuración → Integraciones. Se muestra una sola vez: guárdala como una contraseña. Mándala en cada petición:
curl https://kotiza.co/api/v1/me \ -H "Authorization: Bearer kz_live_…"
Límite: 120 peticiones por minuto por llave. Los errores tienen siempre la misma forma: { "error": { "code": "…", "message": "…" } } (401 llave inválida, 402 plan sin API, 404 no existe, 422 datos inválidos, 429 límite).
2. Endpoints
Las listas vienen de la más nueva a la más vieja: { "data": [...], "has_more": true, "next_cursor": 812 }. Para la página siguiente, ?starting_after=812. ?limit= entre 1 y 100 (50 por defecto). Los montos van en pesos colombianos; si la cotización está en dólares, currency es USD y exchange_rate trae la TRM con que se cotizó.
GET /api/v1/me
GET /api/v1/quotations
Filtros: status (borrador, enviada, en_revision, aprobada, rechazada, vencida), number, client_id, updated_since y created_since (fecha ISO 8601).
Cada cotización trae el cliente, las líneas como las ve el cliente (un consolidado es una línea), los totales (subtotal, cargos, descuento, IVA, total), el link de aprobación, la fecha de aprobación y la factura. Nunca trae costos ni márgenes.
GET /api/v1/quotations/:id
GET /api/v1/clients
tax_id (NIT), email, created_since.GET /api/v1/clients/:id
POST /api/v1/clients
Crea un cliente. Campos: name (obligatorio), id_type (NIT por defecto), tax_id, contact, email, phone, city, address.
Si ya existe un cliente con ese NIT responde 409 con su client_id, para que lo uses en vez de duplicarlo.
GET /api/v1/products
3. Webhooks
Agrega la URL en Configuración → Integraciones o por API. kotiza le hace un POST con el evento apenas pasa:
quotation.created: cotización creada.quotation.sent: cotización enviada.quotation.approved: cotización aprobada.quotation.rejected: cotización rechazada.quotation.status_changed: cualquier cambio de estado.quotation.invoiced: cotización facturada.client.created: cliente creado.
POST https://tu-servidor.com/kotiza
Kotiza-Event: quotation.approved
Kotiza-Event-Id: evt_3f2a…
Kotiza-Signature: t=1793300000,v1=5d41402a…
{
"id": "evt_3f2a…",
"type": "quotation.approved",
"created_at": "2026-10-29T20:15:00.000Z",
"data": { "quotation": { "id": 812, "number": "CO-1042", "status": "aprobada", … }, "previous_status": "enviada" }
}Verifica la firma: calcula HMAC-SHA256 con el secreto del webhook sobre t.cuerpo (el valor t, un punto y el cuerpo tal cual llegó) y compáralo con v1. Rechaza las firmas con más de 5 minutos.
// Node.js
const [t, v1] = sig.split(",").map((p) => p.split("=")[1]);
const esperado = crypto.createHmac("sha256", SECRETO).update(`${t}.${cuerpo}`).digest("hex");
const valida = esperado === v1 && Date.now() / 1000 - Number(t) < 300;Responde 2xx en menos de 10 segundos. Si no, reintentamos con espera creciente durante unos días, con el mismo id de evento: úsalo para no procesar dos veces lo mismo.
POST /api/v1/webhooks
{ "url": "https://…", "events": ["quotation.approved"] }. Devuelve el id y el secret (una sola vez). Sin events recibe todos.GET /api/v1/webhooks
DELETE /api/v1/webhooks/:id
¿Dudas o necesitas algo que no está aquí? Escríbenos a asistencia@kotiza.co.