KalendarIAopenapi.json

API de KalendarIA

REST + JSON, webhooks firmados y servidor MCP. Incluida en todos los planes.

Empezar en 3 pasos

  1. En el panel, API y webhooks → crea una clave (kal_live_…).
  2. Consulta las horas libres de un servicio.
  3. Crea la reserva con uno de los start devueltos.
curl https://staging.164-132-79-6.sslip.io/api/v1/services -H "Authorization: Bearer kal_live_XXXX"

curl "https://staging.164-132-79-6.sslip.io/api/v1/availability?service_id=svc_…&from=2026-11-02&to=2026-11-08" \
  -H "Authorization: Bearer kal_live_XXXX"

curl -X POST https://staging.164-132-79-6.sslip.io/api/v1/bookings \
  -H "Authorization: Bearer kal_live_XXXX" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c…" \
  -d '{"service_id":"svc_…","start":"2026-11-03T13:00:00.000Z",
       "customer":{"name":"Ana Pérez","email":"ana@ejemplo.com","phone":"+56912345678"}}'

Convenciones

Endpoints

Cuenta

GET/accountDatos del negocio

Catálogo

GET/servicesListar servicios
POST/servicesCrear servicio
GET/services/{id}Ver servicio
PATCH/services/{id}Actualizar servicio
DELETE/services/{id}Eliminar servicio (si tiene reservas se archiva)
GET/staffListar profesionales (con horario semanal)
GET/resourcesListar recursos (boxes, sillones, salas, equipos)

Disponibilidad

GET/availabilityHoras libres de un servicio

Reservas

POST/holdsRetener una hora (10 minutos)
GET/bookingsListar reservas
POST/bookingsCrear reserva
GET/bookings/{id}Ver reserva
POST/bookings/{id}/cancelCancelar
POST/bookings/{id}/rescheduleCambiar hora
POST/bookings/{id}/confirmAprobar (pending_approval → confirmed)
POST/bookings/{id}/completeMarcar como atendida
POST/bookings/{id}/no_showMarcar como no asistió

Clientes

GET/customersListar o buscar clientes
POST/customersCrear o actualizar cliente (por email/teléfono)
POST/customers/importAlta masiva (hasta 1.000): deduplica por email/teléfono y solo completa datos vacíos de los que ya existen
GET/customers/{id}Ver cliente
PATCH/customers/{id}Actualizar cliente
DELETE/customers/{id}Suprimir datos personales (RGPD / Ley 21.719)

Descuentos

GET/discount_codesListar códigos de descuento (con usos)
POST/discount_codesCrear código: kind percent (1–100) o amount (unidades mínimas), service_ids, valid_from/until, max_uses, max_per_customer, first_visit_only

Reseñas

GET/reviewsReseñas de clientes (status: published, pending, hidden)

Tarjetas regalo

GET/gift_cardsListar tarjetas regalo
POST/gift_cardsCrear (vender) una tarjeta regalo
GET/gift_cards/{code}Consultar saldo por código (ABCD-EFGH) o id
POST/gift_cards/{code}/redeemCanjear saldo

Webhooks

GET/webhook_endpointsListar webhooks
POST/webhook_endpointsSuscribir un webhook (REST hook)
DELETE/webhook_endpoints/{id}Eliminar webhook
GET/webhook_endpoints/{id}/deliveriesÚltimas entregas de un webhook
GET/eventsRegistro de eventos (reconciliación)
GET/events/{id}Ver evento

Esquemas completos en openapi.json (impórtalo en Postman, Insomnia o un generador de SDK).

Flujo con retención (checkout, WhatsApp)

Retén la hora 10 minutos mientras el cliente termina y confírmala después: nadie más puede tomarla mientras tanto.

POST /api/v1/holds     {"service_id":"svc_…","start":"2026-11-03T13:00:00.000Z"}
→ {"id":"bkg_…","object":"hold","status":"held","hold_expires_at":"…"}

POST /api/v1/bookings  {"hold_id":"bkg_…","customer":{"name":"Ana","phone":"+34612345678"}}
→ {"id":"bkg_…","status":"confirmed","manage_url":"…"}

Webhooks

Formato Standard Webhooks: cabeceras webhook-id, webhook-timestamp y webhook-signature (v1,<base64>). Firma HMAC-SHA256 de id.timestamp.cuerpo con el secreto (whsec_…, se decodifica en base64). Reintentos durante ~42 h; responde 2xx en menos de 10 s.

// Node.js
import crypto from "node:crypto";
function verify(secret, headers, rawBody) {
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const msg = `${headers["webhook-id"]}.${headers["webhook-timestamp"]}.${rawBody}`;
  const sig = crypto.createHmac("sha256", key).update(msg).digest("base64");
  const fresh = Math.abs(Date.now() / 1000 - Number(headers["webhook-timestamp"])) < 300;
  return fresh && headers["webhook-signature"].split(" ").includes(`v1,${sig}`);
}

Eventos:

booking.heldbooking.createdbooking.confirmedbooking.pending_approvalbooking.pending_paymentbooking.rescheduledbooking.canceledbooking.completedbooking.no_showbooking.checked_inbooking.expiredpayment.succeededpayment.failedpayment.refundedgift_card.purchasedreview.createdcustomer.createdcustomer.updatedwaitlist.joinedwaitlist.notified

Si se pierde alguno, GET /api/v1/events devuelve el registro completo para reconciliar.

Servidor MCP (agentes de IA)

Streamable HTTP en https://staging.164-132-79-6.sslip.io/api/mcp. Herramientas: datos del negocio, servicios, horas libres, crear, mover, cancelar y aprobar reservas, marcar asistencia, buscar clientes y resumen del día.

Claude y ChatGPT (OAuth 2.1, sin claves): añade un conector personalizado con la URL https://staging.164-132-79-6.sslip.io/api/mcp. El cliente descubre el servidor de autorización en /.well-known/oauth-protected-resource/api/mcp, se registra solo (RFC 7591) y te pide entrar en KalendarIA y elegir el negocio. Las apps conectadas se ven y se desconectan en Panel → Desarrolladores.

Con clave de API (Claude Code, scripts, n8n):

claude mcp add --transport http kalendaria https://staging.164-132-79-6.sslip.io/api/mcp \
  --header "Authorization: Bearer kal_live_XXXX"

SDK para JavaScript / TypeScript

Un archivo sin dependencias (Node 18+, Deno, Bun, navegador) con tipos: kalendaria.mjs · kalendaria.d.ts. Pone la Idempotency-Key en cada escritura, reintenta 429/5xx con la misma clave y lanza KalendariaError con el code estable.

import { Kalendaria, verifyWebhook } from "https://staging.164-132-79-6.sslip.io/sdk/kalendaria.mjs";

const kal = new Kalendaria({ apiKey: process.env.KALENDARIA_KEY });
const { days } = await kal.availability({ service_id: "svc_…", from: "2026-11-03" });
const booking = await kal.bookings.create({
  service_id: "svc_…", start: days[0].slots[0].start,
  customer: { name: "Ana Pérez", email: "ana@example.com" },
});
for await (const b of kal.paginate((q) => kal.bookings.list(q))) console.log(b.id);

// En tu endpoint de webhooks (cuerpo sin parsear):
const event = await verifyWebhook(process.env.WHSEC, req.headers, rawBody);

MCP público de reservas (para clientes)

Cada negocio tiene un servidor MCP sin login en https://staging.164-132-79-6.sslip.io/api/mcp/public/<slug> con cuatro herramientas: get_business, list_services, get_availability (máximo 14 días) y create_booking (exige nombre, email, teléfono y privacy_consent: true; devuelve el enlace de gestión y, si hay abono, el de pago). Mismos límites anti-abuso que la página pública; no expone clientes ni datos internos.

Zapier, Make y n8n

KalendarIA usa el patrón «REST Hooks»: la herramienta se suscribe a un evento creando un webhook y lo borra al desactivar el escenario. Autenticación: cabecera Authorization: Bearer kal_live_… (o kal_test_… para probar sin tocar tu agenda real).

En n8n: nodo «Webhook» como disparador + nodo «HTTP Request» para suscribirlo una vez; o «HTTP Request» con credencial «Header Auth». En Make: módulo «Webhooks → Custom webhook» y registra su URL con la llamada de arriba. En Zapier: «Webhooks by Zapier → Catch Hook» y regístrala igual (la app oficial de Zapier está en camino).

Widget

<div data-kalendaria="tu-negocio"></div>
<script src="https://staging.164-132-79-6.sslip.io/embed.js" async></script>

<!-- o un botón flotante -->
<script src="https://staging.164-132-79-6.sslip.io/embed.js" data-kalendaria-button="tu-negocio" data-label="Reservar" async></script>

Pasa los UTM, fbclid, gclid y las cookies _fbp/_fbc/_ga a la reserva y, al reservar, lanza dataLayer.push({event: "booking_created"}) y el evento Schedule del píxel de Meta con eventID = id de la reserva.