Files
bot_palmas/API-ERP.md
T
2026-06-12 12:18:21 -05:00

17 KiB

Documentación API — Integración ERP ↔ bot-palmas360

Índice

  1. Autenticación
  2. Endpoints que el ERP debe exponer
  3. Endpoints que el ERP debe consumir
  4. Flujo completo
  5. Implementación de referencia en PHP

1. Autenticación

Todas las llamadas entre el bot y el ERP usan API Key via header:

X-API-Key: tu_api_key_secreta

Cada empresa tiene su propia api_key configurada en la tabla companies. Esta misma key la usa el bot para autenticarse cuando llama al ERP, y el ERP la usa para autenticarse cuando llama al bot.


2. Endpoints que el ERP debe exponer

2.1 Lista de empresas (sincronización)

Usado por el bot para obtener la lista completa de empresas con sus números y URLs.

GET {ERP_SYNC_API_URL}

Autenticación: Authorization: Bearer {ERP_SYNC_API_KEY} (configurado en .env)

Respuesta exitosa (200):

[
  {
    "name": "Empresa Uno",
    "display_name": "Empresa Uno S.A.S.",
    "phone_number_id": "384710295638401",
    "display_phone": "573001234567",
    "api_base_url": "https://erp.empresauno.com/api",
    "api_key": "key_secreta_empresa_1",
    "bot_type": "hybrid",
    "requires_approval": true,
    "is_active": true,
    "config_json": {
      "commands": {
        "menu": "show_main",
        "info": "show_info",
        "contacto": "show_contact",
        "horario": "show_hours"
      },
      "menus": {
        "show_main": {
          "type": "list",
          "header": "Bienvenido a Empresa Uno",
          "body": "Selecciona una opción:",
          "footer": "Empresa Uno",
          "button": "Ver opciones",
          "sections": [
            {
              "title": "Información",
              "rows": [
                { "id": "servicios", "title": "Servicios", "description": "Nuestros servicios disponibles" },
                { "id": "contacto", "title": "Contacto", "description": "Habla con un asesor" }
              ]
            }
          ]
        }
      },
      "flows": {
        "servicios": { "type": "text", "message": "Ofrecemos servicios de consultoría y desarrollo." },
        "contacto": { "type": "function", "function": "forward_to_ai" }
      },
      "ai_prompt": "Eres el asistente virtual de Empresa Uno. Responde preguntas sobre servicios.",
      "fallback": "No entendí. Escribe 'menu' para ver opciones.",
      "approval_webhook": "https://erp.empresauno.com/api/approval-notify"
    }
  }
]

Nota: El bot almacena estos datos localmente en la tabla companies. El campo config_json define el comportamiento del bot normal, mensajes de IA, menús interactivos y webhooks de notificación.


2.2 Recepción de mensajes entrantes

El bot envía aquí cada mensaje que llega por WhatsApp.

POST {empresa.api_base_url}/webhook/incoming

Autenticación: X-API-Key: {empresa.api_key}

Payload:

{
  "company_id": 1,
  "from": "573001234567",
  "name": "Juan Pérez",
  "message_id": "wamid.HBgNNTczMDAxMjM0NTY3FQIAERgSN0QzQjFGQ0YyRjU3OTJGMwA=",
  "type": "text",
  "content": "Hola, quiero información sobre los precios",
  "media_id": null,
  "timestamp": 1717600000,
  "phone_number_id": "384710295638401",
  "display_phone": "573001234567",
  "raw_payload": "{...payload completo de Meta...}"
}

Respuesta esperada (200):

{
  "status": "received"
}

Importante: El bot responderá 200 a Meta inmediatamente después de recibir el mensaje. La respuesta al usuario la maneja el bot según la configuración de la empresa (respuesta automática, IA, o cola de aprobación).


2.3 Estados de mensajes

El bot notifica aquí cuando cambia el estado de un mensaje enviado.

POST {empresa.api_base_url}/webhook/status

Autenticación: X-API-Key: {empresa.api_key}

Payload:

{
  "company_id": 1,
  "message_id": "wamid.HBgNNTczMDAxMjM0NTY3FQIAERgSN0QzQjFGQ0YyRjU3OTJGMwA=",
  "status": "sent",
  "recipient": "573001234567",
  "timestamp": 1717600000,
  "errors": null
}

Posibles valores de status: sent, delivered, read, failed

Cuando es failed, el campo errors contiene detalles del error:

{
  "status": "failed",
  "errors": [
    {
      "code": 131026,
      "title": "Message undeliverable",
      "details": "Phone number has unsubscribed from the channel"
    }
  ]
}

Respuesta esperada (200):

{
  "status": "received"
}

2.4 Health check (opcional pero recomendado)

Usado por el monitor de salud del bot para verificar disponibilidad.

GET {empresa.api_base_url}/health

Autenticación: X-API-Key: {empresa.api_key} (opcional en este endpoint)

No requiere payload. El bot solo verifica que responda HTTP 200.

Respuesta esperada (200):

{
  "status": "ok",
  "service": "erp-empresa-uno",
  "timestamp": "2026-06-12T10:00:00Z"
}

3. Endpoints que el ERP debe consumir

3.1 Enviar mensaje directo

POST https://bot.palmas360.com/api/send-direct

Autenticación: X-API-Key: {api_key de tu empresa}

Usa este endpoint cuando quieras enviar un mensaje a un cliente desde el ERP (ej: un asesor responde manualmente desde tu dashboard).

Payload (texto):

{
  "to": "573001234567",
  "type": "text",
  "text": "Hola Juan, gracias por escribirnos. Te comparto nuestros precios: Plan Básico $49/mes, Plan Premium $99/mes."
}

Payload (imagen):

{
  "to": "573001234567",
  "type": "image",
  "media_id": "123456789", 
  "url": "https://tuservidor.com/imagen.jpg",
  "caption": "Nuestro catálogo de productos"
}

Envia media_id (ID de Meta) o url (URL pública). Si envías ambos, prioriza media_id.

Payload (template):

{
  "to": "573001234567",
  "type": "template",
  "template_name": "bienvenida",
  "components": [
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "Juan" }
      ]
    }
  ]
}

Respuesta exitosa (200):

{
  "status": "queued",
  "id": 123
}

El mensaje se encola y se envía en segundo plano. El estado de envío llegará a tu webhook/status.


3.2 Ver mensajes pendientes de aprobación

GET https://bot.palmas360.com/api/pending

Autenticación: X-API-Key: {api_key de tu empresa}

Devuelve todos los mensajes que el bot procesó pero que requieren aprobación humana antes de enviarse.

Respuesta exitosa (200):

{
  "pending": [
    {
      "id": 5,
      "company_id": 1,
      "phone_number": "573001234567",
      "contact_name": "Juan Pérez",
      "incoming_msg": "Quiero saber el precio del servicio premium",
      "bot_response": {
        "action": "send",
        "type": "text",
        "to": "573001234567",
        "payload": "{\"text\":\"El plan Premium cuesta $99/mes. ¿Te gustaría suscribirte?\"}"
      },
      "bot_type": "ai",
      "status": "pending",
      "created_at": "2026-06-12 09:30:00"
    }
  ],
  "total": 1
}

3.3 Aprobar o rechazar mensaje

POST https://bot.palmas360.com/api/approval

Autenticación: X-API-Key: {api_key de tu empresa}

Aprueba o rechaza un mensaje pendiente.

Payload (aprobar):

{
  "pending_id": 5,
  "action": "approve",
  "note": "Aprobado por gerencia"
}

Respuesta (200):

{
  "status": "approved",
  "pending_id": 5,
  "message": "Respuesta aprobada y encolada para envío"
}

Payload (rechazar):

{
  "pending_id": 5,
  "action": "reject",
  "note": "El cliente pidió información incorrecta"
}

Respuesta (200):

{
  "status": "rejected",
  "pending_id": 5,
  "message": "Mensaje rechazado"
}

Cuando apruebas, el bot automáticamente encola la respuesta generada para enviarla al cliente por WhatsApp.


3.4 Listar empresas (opcional)

Si necesitas consultar qué empresas están registradas en el bot:

GET https://bot.palmas360.com/api/companies

Autenticación: X-API-Key: {api_key de tu empresa} (solo empresas activas)

Respuesta (200):

{
  "companies": [
    {
      "id": 1,
      "name": "Empresa Uno",
      "display_name": "Empresa Uno S.A.S.",
      "phone_number_id": "384710295638401",
      "display_phone": "573001234567",
      "bot_type": "hybrid",
      "requires_approval": true,
      "is_active": true
    }
  ]
}

4. Flujo completo

Cliente WhatsApp                    bot-palmas360                      ERP Palmas360
     │                                    │                                  │
     │  Mensaje de texto                  │                                  │
     │ ─────────────────────────────────► │                                  │
     │                                    │                                  │
     │                                    │ POST /webhook/incoming           │
     │                                    │ ──────────────────────────────►   │
     │                                    │ ◄─── { status: "received" } ─── │
     │                                    │                                  │
     │                                    │ BotRouter decide:                │
     │                                    │ ├─ Respuesta directa             │
     │                                    │ │  → encola en outbound_queue    │
     │                                    │ │  → WhatsAppSender envía        │
     │                                    │ │  → POST /webhook/status        │
     │                                    │ │  ──────────────────────────►   │
     │                                    │ │                                │
     │  ◄─── Respuesta IA/Bot ────────── │ │                                │
     │                                    │ │                                │
     │                                    │ └─ requires_approval = true      │
     │                                    │    → guarda en pending_approval  │
     │                                    │    → notifica vía webhook (opc)  │
     │                                    │                                  │
     │                                    │    GET /api/pending              │
     │                                    │ ◄────────────────────────────     │
     │                                    │    ── lista pendientes ──────►   │
     │                                    │                                  │
     │                                    │    POST /api/approval            │
     │                                    │ ◄────────────────────────────     │
     │                                    │    ── approve/reject ─────────►  │
     │                                    │                                  │
     │                                    │    (si approve)                  │
     │  ◄─── Respuesta aprobada ──────── │    → outbound_queue              │
     │                                    │    → WhatsAppSender envía        │
     │                                    │                                  │
     │                                    │    ERP inicia conversación       │
     │                                    │    POST /api/send-direct         │
     │                                    │ ◄────────────────────────────     │
     │  ◄─── Mensaje del asesor ──────── │    ── mensaje outbound ────────►  │
     │                                    │                                  │

5. Implementación de referencia en PHP

<?php
declare(strict_types=1);

/**
 * Cliente de ejemplo para que el ERP se comunique con bot-palmas360
 */
class BotPalmasClient
{
    private string $baseUrl;
    private string $apiKey;

    public function __construct(string $baseUrl, string $apiKey)
    {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->apiKey  = $apiKey;
    }

    /**
     * Enviar un mensaje de texto
     */
    public function sendText(string $to, string $text): array
    {
        return $this->post('/api/send-direct', [
            'to'   => $to,
            'type' => 'text',
            'text' => $text,
        ]);
    }

    /**
     * Enviar una imagen
     */
    public function sendImage(string $to, string $url, ?string $caption = null): array
    {
        $payload = [
            'to'   => $to,
            'type' => 'image',
            'url'  => $url,
        ];
        if ($caption !== null) {
            $payload['caption'] = $caption;
        }
        return $this->post('/api/send-direct', $payload);
    }

    /**
     * Enviar una plantilla (template)
     */
    public function sendTemplate(string $to, string $templateName, array $components = []): array
    {
        return $this->post('/api/send-direct', [
            'to'            => $to,
            'type'          => 'template',
            'template_name' => $templateName,
            'components'    => $components,
        ]);
    }

    /**
     * Obtener mensajes pendientes de aprobación
     */
    public function getPending(): array
    {
        return $this->get('/api/pending');
    }

    /**
     * Aprobar un mensaje pendiente
     */
    public function approvePending(int $pendingId, ?string $note = null): array
    {
        return $this->post('/api/approval', [
            'pending_id' => $pendingId,
            'action'     => 'approve',
            'note'       => $note,
        ]);
    }

    /**
     * Rechazar un mensaje pendiente
     */
    public function rejectPending(int $pendingId, ?string $note = null): array
    {
        return $this->post('/api/approval', [
            'pending_id' => $pendingId,
            'action'     => 'reject',
            'note'       => $note,
        ]);
    }

    // ─── Helpers HTTP ───────────────────────────────────────────────────────

    private function get(string $path): array
    {
        return $this->request('GET', $path);
    }

    private function post(string $path, array $data): array
    {
        return $this->request('POST', $path, $data);
    }

    private function request(string $method, string $path, ?array $data = null): array
    {
        $url = $this->baseUrl . $path;

        $ch = curl_init($url);
        $headers = [
            'X-API-Key: ' . $this->apiKey,
            'Content-Type: application/json',
            'Accept: application/json',
        ];

        $options = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER     => $headers,
            CURLOPT_TIMEOUT        => 15,
        ];

        if ($method === 'POST') {
            $options[CURLOPT_POST]       = true;
            $options[CURLOPT_POSTFIELDS] = json_encode($data ?? []);
        }

        curl_setopt_array($ch, $options);
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error    = curl_error($ch);
        curl_close($ch);

        if ($error !== '') {
            return ['success' => false, 'error' => $error];
        }

        $decoded = json_decode($response, true);

        return [
            'success'   => $httpCode >= 200 && $httpCode < 300,
            'http_code' => $httpCode,
            'data'      => $decoded ?? $response,
        ];
    }
}

// ─── Ejemplo de uso ────────────────────────────────────────────────────────

$bot = new BotPalmasClient('https://bot.palmas360.com', 'api_key_de_mi_empresa');

// Enviar mensaje
$result = $bot->sendText('573001234567', 'Hola, gracias por escribirnos');
print_r($result);

// Ver pendientes
$pending = $bot->getPending();
foreach ($pending['pending'] ?? [] as $item) {
    echo "Pendiente #{$item['id']}: {$item['incoming_msg']}\n";
}

// Aprobar el primero
if (!empty($pending['pending'])) {
    $bot->approvePending((int)$pending['pending'][0]['id'], 'Aprobado desde ERP');
}

Resumen rápido de endpoints

Dirección Qué hace
ERP → Bot
POST /api/send-direct Enviar mensaje WhatsApp
GET /api/pending Ver pendientes de aprobación
POST /api/approval Aprobar/rechazar pendiente
Bot → ERP
POST /webhook/incoming Recibir mensaje entrante
POST /webhook/status Recibir estado de envío
GET /health Health check