API por-cliente para que tu app reciba los eventos de WhatsApp de tu negocio en tiempo real (webhooks) y pueda tomar control de una conversación (pausar el bot) y responder desde tu sistema.
https://macondoia.co/api/v1Authorization: Bearer mck_live_… (un token por cliente).Tu token y el webhook se provisionan desde el panel de Macondo (Ajustes → API). El token se muestra una sola vez; guárdalo seguro. Cada token está atado a un solo cliente y solo ve/actúa sobre las conversaciones de ese cliente.
curl https://macondoia.co/api/v1/conversations \
-H "Authorization: Bearer mck_live_xxxxxxxxxxxxxxxxxxxxxxxx"Errores: 401 unauthorized (token ausente/inválido/revocado), 403 forbidden (al token le falta el scope). Scopes: messages:read, messages:send, control:write.
Rate limit: 120 solicitudes/min por key. Al superarlo: 429 rate_limited con header Retry-After: 60.
Forma de error estándar:
{ "error": { "code": "not_found", "message": "Conversación no encontrada." } }Lista las conversaciones del cliente con su estado de control.
{
"conversations": [
{
"id": "3260ee5d-…",
"contact_name": "Fito Segrera",
"phone_number": "+57 314 766 8729",
"status": "active",
"last_message_text": "hola de nuevo",
"last_active_at": "2026-06-09T20:45:00Z",
"control": "bot" // bot | handoff_pending | human
}
]
}Historial de mensajes (orden cronológico). since es opcional.
{
"messages": [
{ "kapso_message_id": "wamid…", "direction": "inbound", "type": "text",
"text": "hola", "image_url": null, "media_url": null,
"status": "delivered", "wa_timestamp": "2026-06-09T20:44:00Z" }
]
}Estado de control + ventana de envío de 24h.
{ "state": "bot", "reason": null, "handoff_reason": null,
"window_open": true, "window_expires_at": "2026-06-10T20:44:00Z" }Pausa el bot para esa conversación (su ejecución pasa a handoff) y la marca human. Idempotente.
Devuelve el control al bot (reanuda en el próximo mensaje del cliente).
Envía un mensaje de texto. Si la conversación no está en human, toma el control automáticamente antes de enviar (así el bot y tu app nunca responden a la vez).
curl -X POST https://macondoia.co/api/v1/conversations/<id>/messages \
-H "Authorization: Bearer mck_live_…" -H "Content-Type: application/json" \
-d '{"text":"¡Hola! Te ayudo con eso."}'200 → { "ok": true, "message": "Mensaje enviado." }409 window_closed → fuera de la ventana de 24h de WhatsApp (Meta exige plantilla; no soportado en v1).422 invalid_request / send_failed → falta text u otro error.Registras una URL y un secret (en Ajustes → API). Macondo te envía un POST por cada evento suscrito.
| Evento | Cuándo |
|---|---|
message.received | mensaje entrante del cliente |
message.sent | mensaje saliente (bot, operador o tu app) |
handoff.requested | el bot pidió ayuda (handoff_to_human) → toca intervenir |
control.changed | cambió el estado de control |
Headers de cada entrega:
X-Macondo-Event: nombre del evento.X-Macondo-Signature: sha256=<hmac_sha256(secret, raw_body)>.X-Idempotency-Key: id único del evento (deduplica reintentos).// message.received / message.sent
{ "event": "message.received", "conversation_id": "3260ee5d-…", "client_id": "…",
"message": { "id": "wamid…", "direction": "inbound", "type": "text", "text": "hola",
"image_url": null, "media_url": null, "status": "received",
"timestamp": "2026-06-09T20:44:00Z" } }
// handoff.requested
{ "event": "handoff.requested", "conversation_id": "3260ee5d-…", "client_id": "…",
"reason": "el cliente pidió hablar con una persona" }
// control.changed
{ "event": "control.changed", "conversation_id": "3260ee5d-…", "client_id": "…",
"state": "human" }Verificar la firma (Node.js):
import crypto from "node:crypto";
function verify(req, secret) {
const raw = req.rawBody; // el cuerpo EXACTO recibido, sin re-serializar
const expected = "sha256=" +
crypto.createHmac("sha256", secret).update(raw).digest("hex");
const got = req.headers["x-macondo-signature"] || "";
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got));
}Responde 200 rápido y trata las entregas como at-least-once (deduplica por X-Idempotency-Key). Reintentos: ante no-2xx reintentamos con backoff (~30s, 2m, 10m, 30m); tras 5 intentos la entrega se marca dead. Si tu endpoint acumula muchas fallas seguidas, se desactiva (se re-activa desde Ajustes).
handoff.requested (o decides intervenir).POST …/take-control → el bot queda pausado solo en esa conversación.POST …/messages para responder (dentro de la ventana de 24h). Tus envíos vuelven como message.sent.POST …/release-control al terminar → el bot retoma en el próximo mensaje.La API es para clientes de producción con su propio número de WhatsApp. Versionado: /api/v1.