← Macondo IAAPI v1

Macondo API

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.

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.

1. Autenticación

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." } }

2. Endpoints REST

GET /conversations

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
    }
  ]
}

GET /conversations/{id}/messages?since=<ISO8601>

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" }
  ]
}

GET /conversations/{id}/control

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" }

POST /conversations/{id}/take-control

Pausa el bot para esa conversación (su ejecución pasa a handoff) y la marca human. Idempotente.

POST /conversations/{id}/release-control

Devuelve el control al bot (reanuda en el próximo mensaje del cliente).

POST /conversations/{id}/messages

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."}'

3. Webhooks salientes

Registras una URL y un secret (en Ajustes → API). Macondo te envía un POST por cada evento suscrito.

EventoCuándo
message.receivedmensaje entrante del cliente
message.sentmensaje saliente (bot, operador o tu app)
handoff.requestedel bot pidió ayuda (handoff_to_human) → toca intervenir
control.changedcambió el estado de control

Headers de cada entrega:

// 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).

4. Flujo "tomar control"

  1. Recibes handoff.requested (o decides intervenir).
  2. POST …/take-control → el bot queda pausado solo en esa conversación.
  3. POST …/messages para responder (dentro de la ventana de 24h). Tus envíos vuelven como message.sent.
  4. POST …/release-control al terminar → el bot retoma en el próximo mensaje.
Tokens / rotación: genera, lista y revoca tokens en Ajustes → API. La revocación es inmediata. Para rotar: genera una key nueva, despliégala en tu app, y luego revoca la vieja (cero downtime).

La API es para clientes de producción con su propio número de WhatsApp. Versionado: /api/v1.