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

590 lines
17 KiB
Markdown

# Documentación API — Integración ERP ↔ bot-palmas360
## Índice
1. [Autenticación](#1-autenticación)
2. [Endpoints que el ERP debe exponer](#2-endpoints-que-el-erp-debe-exponer)
3. [Endpoints que el ERP debe consumir](#3-endpoints-que-el-erp-debe-consumir)
4. [Flujo completo](#4-flujo-completo)
5. [Implementación de referencia en PHP](#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):**
```json
[
{
"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:**
```json
{
"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):**
```json
{
"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:**
```json
{
"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:
```json
{
"status": "failed",
"errors": [
{
"code": 131026,
"title": "Message undeliverable",
"details": "Phone number has unsubscribed from the channel"
}
]
}
```
**Respuesta esperada (200):**
```json
{
"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):**
```json
{
"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):**
```json
{
"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):**
```json
{
"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):**
```json
{
"to": "573001234567",
"type": "template",
"template_name": "bienvenida",
"components": [
{
"type": "body",
"parameters": [
{ "type": "text", "text": "Juan" }
]
}
]
}
```
**Respuesta exitosa (200):**
```json
{
"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):**
```json
{
"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):**
```json
{
"pending_id": 5,
"action": "approve",
"note": "Aprobado por gerencia"
}
```
**Respuesta (200):**
```json
{
"status": "approved",
"pending_id": 5,
"message": "Respuesta aprobada y encolada para envío"
}
```
**Payload (rechazar):**
```json
{
"pending_id": 5,
"action": "reject",
"note": "El cliente pidió información incorrecta"
}
```
**Respuesta (200):**
```json
{
"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):**
```json
{
"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
<?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 |