17 KiB
Documentación API — Integración ERP ↔ bot-palmas360
Índice
- Autenticación
- Endpoints que el ERP debe exponer
- Endpoints que el ERP debe consumir
- Flujo completo
- 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 campoconfig_jsondefine 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) ourl(URL pública). Si envías ambos, priorizamedia_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 |