# PROMPT – Backend Node.js para plataforma SaaS WhatsApp

## Contexto

Ya existe un sistema en producción compuesto por:

- **API PHP** que gestiona toda la lógica de WhatsApp Business (envío de mensajes, webhooks, templates, reportes, campañas). Esta API ya es **multi-tenant**: aísla datos por `company_id` y se autentica con API Key + API Secret.
- **Base de datos MySQL** administrada exclusivamente por la API PHP. El backend Node.js **no accede a la base de datos directamente**; toda interacción es mediante JSON a través de la API PHP.
- **WhatsApp Cloud API (Meta Graph API v18.0)** — la comunicación con Meta la maneja la API PHP internamente.

La arquitectura es:

```
Flutter / Bootstrap Frontend
        ↓ (HTTP JSON)
   Node.js Backend (Express)
        ↓ (HTTP JSON)
      API PHP Multi-Tenant
        ↓
   MySQL + WhatsApp Cloud API
```

El backend Node.js actúa como **capa intermedia** entre el frontend y la API PHP. Su rol es:

1. Gestionar sesiones/JWT para los usuarios del frontend.
2. Servir como proxy inteligente hacia la API PHP, reenviando las credenciales de empresa.
3. Agregar lógica de negocio propia del frontend (perfiles de usuario, roles, preferencias).
4. Servir el frontend Bootstrap (o proveer API REST para Flutter).

---

## Autenticación contra la API PHP

**Flujo de autenticación (2 pasos):**

1. **Login:** El backend solo conoce `user` + `password` de un usuario CRM. Llama a `?action=login` con esas credenciales.
2. **Respuesta:** La API PHP valida contra `crm_users` (bcrypt), resuelve la empresa (`company_id`), y devuelve el `api_key` + `api_secret` de esa empresa.
3. **Uso posterior:** El backend guarda `api_key`/`api_secret` en memoria/sesión y los envía como headers en todas las demás peticiones.

```
Headers requeridos en TODAS las peticiones (excepto login):
  X-API-Key: {api_key recibido del login}
  X-API-Secret: {api_secret recibido del login}

Header adicional para endpoints protegidos por rol (create_user, update_user, delete_user, update_company):
  X-User-Id: {user_id recibido del login}
```

La API PHP resuelve automáticamente la empresa y filtra todos los datos por `company_id`.

---

## URL Base de la API PHP

```
POST/GET  https://apiwhats.artechsolutions-ec.com/api/api.php?action={ACTION}
```

Todas las acciones se invocan enviando el parámetro `action` en la URL. El body (cuando aplica) es JSON.

---

## Acciones disponibles (Endpoints de la API PHP)

### 1. Login / Autenticación (endpoint público, no requiere headers)

```
POST ?action=login
Content-Type: application/json

Body (usuario/contraseña):
{ "user": "admin", "password": "tu_contraseña" }

Body alternativo (api_key directo):
{ "api_key": "ak-artech-2026-xj9k3m8p7n5q2w1z", "api_secret": "sk-artech-secure-2026-r4t7y2u9i1o3p6a8" }

Response exitosa:
{
  "success": true,
  "company_id": 1,
  "company": "Artech Solutions",
  "slug": "artech",
  "api_key": "ak-artech-2026-xj9k3m8p7n5q2w1z",
  "api_secret": "sk-artech-secure-2026-r4t7y2u9i1o3p6a8",
  "user_id": 1,
  "full_name": "Administrador CRM",
  "role": "admin",
  "message": "Use api_key y api_secret en headers X-API-Key / X-API-Secret para todas las demás llamadas."
}

Response error (401):
{ "error": "Credenciales inválidas" }
```

**Flujo recomendado en Node.js:**
```javascript
// 1. Login al iniciar
const loginRes = await fetch(API_URL + '?action=login', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ user: 'admin', password: 'tu_contraseña' })
});
const { api_key, api_secret } = await loginRes.json();

// 2. Guardar en memoria y usar en todas las demás llamadas
const authHeaders = { 'X-API-Key': api_key, 'X-API-Secret': api_secret };
```

### 2. Dashboard

```
GET ?action=dashboard
Headers: X-API-Key, X-API-Secret

Response:
{
  "success": true,
  "stats": {
    "total_messages": 150,
    "delivered": 120,
    "read": 80,
    "failed": 5,
    "pending": 10,
    "incoming_messages": 45,
    "templates_count": 3,
    "today_messages": 12
  },
  "chart_data": {
    "labels": ["2026-03-04", "2026-03-05", ...],
    "enviados": [10, 15, ...],
    "entregados": [8, 12, ...],
    "leidos": [5, 8, ...],
    "fallidos": [0, 1, ...]
  }
}
```

### 3. Templates

```
GET ?action=templates
Headers: X-API-Key, X-API-Secret

Response:
{
  "success": true,
  "templates": [
    {
      "id": 9,
      "template_id": "842953218285409",
      "name": "bienvenida_cuenta_activa",
      "status": "APPROVED",
      "category": "UTILITY",
      "language": "es",
      "body_text": "Hola {{1}}, tu cuenta está activa...",
      "variable_count": 1,
      "components": [...],
      "synced_at": "2026-03-10 10:00:00"
    }
  ]
}
```

### 4. Sincronizar Templates desde Meta

```
POST ?action=sync_templates
Headers: X-API-Key, X-API-Secret

Response:
{
  "success": true,
  "message": "Templates sincronizados",
  "data": { "total": 3, "new": 0, "updated": 1 }
}
```

### 5. Envío Individual

```
POST ?action=send_individual
Headers: X-API-Key, X-API-Secret
Body:
{
  "phone": "+593999888777",
  "template_id": "842953218285409",
  "parameters": ["Juan", "12345"]
}

Response:
{
  "success": true,
  "message_id": 156,
  "wa_id": "wamid.HBgLNTkzOTk5ODg4Nzc3FQ..."
}
```

### 6. Envío Masivo (CSV)

```
POST ?action=send_bulk
Headers: X-API-Key, X-API-Secret
Body:
{
  "template_id": "842953218285409",
  "csv_data": [
    { "phone": "+593999888777", "var1": "Juan", "var2": "12345" },
    { "phone": "+593999888666", "var1": "María", "var2": "67890" }
  ],
  "delay_seconds": 2
}

Response:
{
  "success": true,
  "results": {
    "total": 2,
    "sent": 2,
    "failed": 0,
    "details": [...]
  }
}
```

### 7. Campañas

#### Listar campañas
```
GET ?action=campaigns_list&page=1&limit=20
Headers: X-API-Key, X-API-Secret

Response:
{
  "success": true,
  "data": {
    "campaigns": [
      {
        "id": 1,
        "name": "Campaña Marzo",
        "template_id": "842953218285409",
        "template_name": "bienvenida_cuenta_activa",
        "status": "draft",
        "total_recipients": 500,
        "sent_count": 0,
        "failed_count": 0,
        "delay_seconds": 2,
        "created_at": "2026-03-10 10:00:00"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 1,
      "total_pages": 1
    }
  }
}
```

#### Crear campaña
```
POST ?action=campaign_create
Headers: X-API-Key, X-API-Secret
Body:
{
  "name": "Campaña Marzo",
  "template_id": "842953218285409",
  "delay_seconds": 2,
  "csv_original_name": "clientes_marzo.csv",
  "recipients": [
    { "phone": "+593999888777", "variables": ["Juan", "12345"] },
    { "phone": "+593999888666", "variables": ["María", "67890"], "header_media_url": "https://..." }
  ]
}

Response:
{
  "success": true,
  "campaign_id": 1,
  "message": "Campaña 'Campaña Marzo' creada con 2 destinatarios"
}
```

#### Detalle de campaña
```
GET ?action=campaign_detail&id=1
Headers: X-API-Key, X-API-Secret

Response:
{
  "success": true,
  "data": {
    "id": 1,
    "name": "Campaña Marzo",
    "template_id": "842953218285409",
    "template_name": "bienvenida_cuenta_activa",
    "status": "draft",
    "total_recipients": 2,
    "sent_count": 0,
    "failed_count": 0,
    "recipient_summary": [
      { "status": "pending", "count": 2 }
    ],
    "recipients": [
      {
        "id": 1,
        "phone_number": "+593999888777",
        "variables": "[\"Juan\",\"12345\"]",
        "status": "pending",
        "error_message": null,
        "sent_at": null,
        "wa_message_id": ""
      }
    ]
  }
}
```

#### Ejecutar envío de campaña
```
POST ?action=campaign_send
Headers: X-API-Key, X-API-Secret
Body: { "campaign_id": 1 }

Response:
{
  "success": true,
  "message": "Campaña completada: 2 enviados, 0 fallidos",
  "sent": 2,
  "failed": 0
}
```

### 8. Historial de conversación

```
GET ?action=conversation_history&phone=+593999888777&limit=50
Headers: X-API-Key, X-API-Secret

Response:
{
  "success": true,
  "phone": "+593999888777",
  "data": [
    {
      "direction": "outgoing",
      "id": 156,
      "wa_id": "wamid.HBgL...",
      "phone": "+593999888777",
      "template_name": "bienvenida_cuenta_activa",
      "body": "Hola Juan, tu cuenta está activa",
      "timestamp": "2026-03-10 10:00:00",
      "status": "read",
      "message_type": "template",
      "profile_name": null
    },
    {
      "direction": "incoming",
      "id": 45,
      "wa_id": "wamid.HBgL...",
      "phone": "+593999888777",
      "template_name": null,
      "body": "Gracias por la información",
      "timestamp": "2026-03-10 10:05:00",
      "status": "received",
      "message_type": "text",
      "profile_name": "Juan Pérez"
    }
  ]
}
```

### 9. Reportes (v2 — con filtros avanzados y paginación)

#### Estadísticas de dashboard
```
GET ?action=dashboard_stats
Headers: X-API-Key, X-API-Secret

Response: { "success": true, "ultimos_7_dias": [...], "hoy": {...} }
```

#### Reportes avanzados (endpoint unificado)
```
GET ?action=reports&type={tipo}&from=2026-03-01&to=2026-03-10
Headers: X-API-Key, X-API-Secret

Tipos disponibles:
- detallado         → Reporte detallado de mensajes (requiere from, to)
- resumen           → Resumen agrupado por fecha/template/categoría (requiere from, to)
- errores           → Reporte de errores (from/to opcionales)
- conversaciones    → Conversaciones por número (requiere from, to)
- entrantes         → Mensajes entrantes (requiere from, to)
- semanal           → Estadísticas últimos 7 días

Filtros opcionales (se agregan como query params o en body JSON):
- campaign_id=1          → Filtrar por campaña específica
- template_name=bienvenida_cuenta_activa → Filtrar por nombre de plantilla
- category=marketing     → Filtrar por categoría (marketing|utility|authentication|service)
- page=1                 → Página actual (default: 1)
- limit=50               → Registros por página (default: 200, max: 1000)

Super-admin (empresa #1) puede consultar reportes de otra empresa:
- filter_company_id=2    → Ver reportes de la empresa 2
```

#### Ejemplo de llamada con filtros
```
GET ?action=reports&type=detallado&from=2026-03-01&to=2026-03-17&campaign_id=1&page=1&limit=50
Headers: X-API-Key, X-API-Secret
```

#### Response (todos los tipos devuelven el mismo formato)
```json
{
  "success": true,
  "company_id": 1,
  "type": "detallado",
  "filters": {
    "date_from": "2026-03-01",
    "date_to": "2026-03-17",
    "campaign_id": "1"
  },
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 150,
    "total_pages": 3
  },
  "total_registros": 150,
  "data": [...]
}
```

#### Campos devueltos por tipo

**detallado:** `id, wa_message_id, numero, template_name, category, campaign_id, campaign_name, envio_creado_en, delivered_at, read_at, last_status, last_status_at, billable, unit_price, currency, market, billed_at`

**resumen:** `fecha, template_name, category, enviados, entregados, leidos, fallidos, cobrables, costo_total, moneda`

**errores:** `telefono, error_code, error_title, ocurrencias, primera_falla, ultima_falla, ultimo_mensaje_id, ultima_plantilla`

**conversaciones:** `phone_number, mensajes_enviados, respuestas_recibidas, total_interacciones, primera_interaccion, ultima_interaccion`

**entrantes:** `id, wa_message_id, numero_cliente, nombre_cliente, tipo_mensaje, contenido, fecha_hora, es_respuesta, responde_a_plantilla, campaign_id, mensaje_enviado_el, media_id, media_mime_type, recibido_sistema`

### 10. Información de la empresa

```
GET ?action=company_info
Headers: X-API-Key, X-API-Secret

Response:
{
  "success": true,
  "company": {
    "id": 1,
    "name": "Artech Solutions",
    "slug": "artech",
    "wa_display_phone": "+593981802659",
    "wa_verified_name": "Artech Solution 2659",
    "graph_api_version": "v18.0",
    "max_messages_per_day": 1000,
    "is_active": 1,
    "created_at": "2026-01-01 00:00:00"
  },
  "api_key": "default",
  "daily_usage": {
    "sent": 12,
    "limit": 1000
  }
}
```

### 11. Super Admin (solo empresa #1)

```
GET ?action=superadmin_stats          → Stats globales de todas las empresas
GET ?action=superadmin_companies      → Lista de todas las empresas
GET ?action=company_users&company_id=5 → Usuarios de una empresa específica
POST ?action=superadmin_create_company → Crear nueva empresa tenant (ver detalle abajo)
```

#### Crear empresa con usuario admin automático
```
POST ?action=superadmin_create_company
Headers: X-API-Key, X-API-Secret (empresa #1 / super-admin)
Body:
{
  "name": "Nueva Empresa",
  "slug": "nueva-empresa",
  "wa_token": "EAAG...",
  "wa_phone_number_id": "123456789",
  "wa_waba_id": "987654321",
  "wa_display_phone": "+593999000111",
  "wa_verified_name": "Nueva Empresa SA",
  "webhook_secret": "mi_webhook_secret",
  "verify_token": "mi_verify_token",
  "admin_email": "admin@nuevaempresa.com",
  "admin_password": "SecurePass123",
  "admin_name": "Admin Nueva Empresa"
}

Campos opcionales para creación automática de usuario admin:
- admin_email    → si se envía junto con admin_password, se crea un usuario admin automáticamente
- admin_password → contraseña del usuario admin (se hashea con bcrypt)
- admin_name     → nombre del admin (default: "{nombre empresa} Admin")

Response (con admin):
{
  "success": true,
  "company_id": 2,
  "api_key": "ak-a1b2c3d4e5f6...",
  "api_secret": "sk-x9y8z7w6v5u4...",
  "message": "Empresa creada exitosamente con API key generada. Usuario admin creado.",
  "admin_user": {
    "user_id": 5,
    "email": "admin@nuevaempresa.com",
    "role": "admin"
  }
}

Response (sin admin, igual que antes):
{
  "success": true,
  "company_id": 2,
  "api_key": "ak-a1b2c3d4e5f6...",
  "api_secret": "sk-x9y8z7w6v5u4...",
  "message": "Empresa creada exitosamente con API key generada."
}
```

### 12. Gestión de Usuarios CRM (CRUD)

La tabla `crm_users` soporta los roles: **`admin`**, **`agent`**, **`viewer`**.

Todos estos endpoints filtran automáticamente por la empresa autenticada (company_id). Un usuario solo puede ver/gestionar usuarios de su propia empresa.

**IMPORTANTE — Control de acceso por rol:**
- `create_user`, `update_user`, `delete_user` → requieren rol **admin**
- `list_users` → accesible por cualquier usuario autenticado
- `company_users` → solo super-admin (empresa #1), lista usuarios de cualquier empresa
- Se debe enviar el header `X-User-Id` en los endpoints protegidos
- La API verifica que el user_id pertenezca a la empresa Y tenga rol admin
- Si el rol es insuficiente → responde **403**

#### Crear usuario
```
POST ?action=create_user
Headers: X-API-Key, X-API-Secret, X-User-Id: {user_id del admin}
Body:
{
  "email": "nuevo@empresa.com",
  "password": "SecurePass123",
  "full_name": "Juan Pérez",
  "role": "agent",
  "username": "jperez"
}

Campos requeridos: email, password, full_name
Campos opcionales:
- role     → "admin" | "agent" | "viewer" (default: "agent")
- username → si no se envía, se usa el email como username

Response (201):
{
  "success": true,
  "user": {
    "id": 5,
    "username": "jperez",
    "email": "nuevo@empresa.com",
    "full_name": "Juan Pérez",
    "role": "agent",
    "is_active": 1,
    "created_at": "2026-03-16 10:00:00",
    "updated_at": "2026-03-16 10:00:00"
  }
}

Errores posibles:
- 400: Campo requerido faltante / Rol inválido
- 409: "Ya existe un usuario con ese email o username."
```

#### Listar usuarios de la empresa
```
GET ?action=list_users
Headers: X-API-Key, X-API-Secret

Response:
{
  "success": true,
  "users": [
    {
      "id": 1,
      "username": "admin",
      "email": "admin@empresa.com",
      "full_name": "Administrador",
      "role": "admin",
      "is_active": 1,
      "created_at": "2026-01-01 00:00:00",
      "updated_at": "2026-03-15 12:00:00"
    }
  ]
}
```

#### Listar usuarios de otra empresa (Super Admin)
```
GET ?action=company_users&company_id=5
Headers: X-API-Key, X-API-Secret (empresa #1)

Response:
{
  "success": true,
  "data": {
    "company_id": 5,
    "company_name": "Empresa Ejemplo",
    "users": [
      {
        "id": 10,
        "username": "jperez",
        "full_name": "Juan Pérez",
        "email": "juan@empresa.com",
        "role": "admin",
        "is_active": 1,
        "created_at": "2026-01-15 10:30:00",
        "updated_at": "2026-03-20 14:22:00"
      }
    ],
    "total": 1
  }
}

Errores:
- 400: company_id faltante / Empresa no encontrada
- 403: Solo super-admin
```

#### Actualizar usuario
```
POST ?action=update_user
Headers: X-API-Key, X-API-Secret, X-User-Id: {user_id del admin}
Body:
{
  "user_id": 5,
  "full_name": "Juan P. García",
  "role": "admin",
  "email": "juan.garcia@empresa.com",
  "password": "NuevaContraseña456",
  "is_active": 1
}

Campo requerido: user_id
Campos editables (todos opcionales):
- email, full_name, role, username, is_active
- password → si se envía, se re-hashea con bcrypt

Response:
{
  "success": true,
  "user": {
    "id": 5,
    "username": "jperez",
    "email": "juan.garcia@empresa.com",
    "full_name": "Juan P. García",
    "role": "admin",
    "is_active": 1,
    "created_at": "2026-03-16 10:00:00",
    "updated_at": "2026-03-16 11:30:00"
  }
}

Errores posibles:
- 400: user_id faltante / Rol inválido
- 404: "Usuario no encontrado en esta empresa."
```

#### Eliminar (desactivar) usuario
```
POST ?action=delete_user
Headers: X-API-Key, X-API-Secret, X-User-Id: {user_id del admin}
Body: { "user_id": 5 }

Nota: Es un SOFT DELETE — pone is_active = 0, no borra el registro.

Response:
{ "success": true, "message": "Usuario desactivado." }

Errores posibles:
- 400: user_id faltante
- 404: "Usuario no encontrado en esta empresa."
```

### 13. Editar Empresa

Permite que una empresa autenticada edite sus propios datos.

**IMPORTANTE:** Requiere rol **admin**. Enviar header `X-User-Id`.

```
POST ?action=update_company
Headers: X-API-Key, X-API-Secret, X-User-Id: {user_id del admin}
Body (todos los campos son opcionales):
{
  "name": "Nuevo Nombre SA",
  "wa_token": "EAAT...",
  "wa_phone_number_id": "817854531405693",
  "wa_waba_id": "799342059122741",
  "wa_display_phone": "+593981802659",
  "wa_verified_name": "Nuevo Nombre Verificado",
  "graph_api_version": "v18.0",
  "webhook_secret": "nuevo_secret",
  "verify_token": "nuevo_verify_token",
  "max_messages_per_day": 5000,
  "is_active": 1
}

Response:
{
  "success": true,
  "message": "Empresa actualizada.",
  "company": {
    "id": 1,
    "name": "Nuevo Nombre SA",
    "slug": "artech",
    "wa_display_phone": "+593981802659",
    "wa_verified_name": "Nuevo Nombre Verificado",
    "max_messages_per_day": 5000,
    "is_active": 1
  }
}

Errores posibles:
- 400: "No se enviaron campos para actualizar."
```

---

## Endpoint directo de envío de template

Además de `api.php`, existe un endpoint directo:

```
POST https://apiwhats.artechsolutions-ec.com/api/send_template.php
Headers: X-API-Key, X-API-Secret (o X-API-Token para legacy)
Body:
{
  "phone": "+593999888777",
  "template": "bienvenida_cuenta_activa",
  "language": "es",
  "components": [
    {
      "type": "body",
      "parameters": [
        { "type": "text", "text": "Juan" }
      ]
    }
  ]
}

Response:
{
  "success": true,
  "wa_response": { "messaging_product": "whatsapp", "messages": [{ "id": "wamid.HBgL..." }] }
}
```

---

### 14. Clonar Empresa (Super Admin)

Duplica una empresa existente copiando su config WA y/o plantillas.

```
POST ?action=clone_company
Headers: X-API-Key, X-API-Secret (empresa #1)
Body:
{
  "source_company_id": 1,
  "new_name": "Empresa Nueva S.A.",
  "new_slug": "empresa-nueva",
  "clone_whatsapp_config": true,
  "clone_templates": true,
  "admin_email": "admin@empresanueva.com",
  "admin_password": "password123",
  "admin_name": "Admin Empresa Nueva"
}

Campos requeridos: source_company_id, new_name, new_slug
Campos opcionales:
- clone_whatsapp_config → copia wa_token, wa_waba_id, etc. (wa_phone_number_id NO se copia, queda NULL)
- clone_templates       → duplica registros de whatsapp_templates
- admin_email/admin_password → crea usuario admin en la nueva empresa
- wa_phone_number_id    → si se proporciona, se asigna directamente (debe ser único)
- wa_token, wa_waba_id  → credenciales WA manuales

IMPORTANTE sobre wa_phone_number_id:
- Si NO se proporciona al clonar, queda NULL (UNIQUE permite múltiples NULLs)
- Con wa_phone_number_id = NULL, la empresa NO puede enviar mensajes
- La respuesta incluye "needs_phone_number_id": true cuando falta configurar
- Usar update_company para asignar el Phone Number ID real de Meta antes de enviar

Response:
{
  "success": true,
  "data": {
    "company": {
      "id": 2,
      "name": "Empresa Nueva S.A.",
      "slug": "empresa-nueva",
      "wa_display_phone": "+593981802659",
      "api_key": "ak-...",
      "api_secret": "sk-..."
    },
    "cloned": {
      "whatsapp_config": true,
      "templates_count": 4,
      "needs_phone_number_id": true
    },
    "admin_user": {
      "id": 5,
      "email": "admin@empresanueva.com",
      "role": "admin"
    }
  }
}
```

**Validación de envío:** Si una empresa intenta enviar mensajes sin `wa_phone_number_id` configurado,
la API devuelve HTTP 200 con `{ "success": false, "message": "La empresa no tiene un Phone Number ID de WhatsApp configurado..." }`.
Esto aplica a: `send_message`, `send_bulk`, `send_template`, `campaign_send`.

### 15. Desactivar Empresa (Soft Delete — Super Admin)

Desactiva una empresa sin borrar datos. Se puede reactivar después con `update_company` enviando `is_active: 1`.

```
POST ?action=deactivate_company
Headers: X-API-Key, X-API-Secret (empresa #1)
Body:
{ "company_id": 5 }

Response:
{
  "success": true,
  "data": {
    "company_id": 5,
    "name": "Empresa Ejemplo",
    "status": "deactivated"
  },
  "message": "Empresa desactivada. Puede reactivarse con update_company."
}

Efectos:
- Empresa: is_active = 0
- API Keys: is_active = 0 (no podrá autenticarse)
- Usuarios CRM: is_active = 0 (no podrán hacer login)
- Los datos (mensajes, campañas, templates) se mantienen intactos

Para reactivar:
POST ?action=update_company con { "is_active": 1 } (requiere credenciales de super-admin)

Errores:
- 400: company_id faltante / Empresa no encontrada / No se puede desactivar empresa #1
- 403: Solo super-admin
```

### 16. Eliminar Empresa Permanentemente (Hard Delete — Super Admin)

Elimina una empresa y TODOS sus datos asociados de la base de datos. **Esta acción es IRREVERSIBLE.**

Requiere confirmación explícita con el campo `confirm`.

```
POST ?action=delete_company
Headers: X-API-Key, X-API-Secret (empresa #1)
Body:
{
  "company_id": 5,
  "confirm": "ELIMINAR_PERMANENTEMENTE"
}

Response:
{
  "success": true,
  "data": {
    "company_id": 5,
    "name": "Empresa Ejemplo",
    "status": "deleted_permanently"
  },
  "message": "Empresa y todos sus datos eliminados permanentemente."
}

Datos que se eliminan en cascada:
- campaign_recipients (destinatarios de campañas)
- template_variable_mapping
- billing_events
- message_status
- incoming_messages
- messages
- whatsapp_templates
- campaigns
- api_keys
- crm_users
- La empresa misma

Errores:
- 400: company_id faltante / confirm incorrecto / Empresa no encontrada / No se puede eliminar empresa #1
- 403: Solo super-admin
- 500: Error en el borrado
```

---

## Webhook (referencia)

La API PHP recibe webhooks de Meta en:
```
POST https://apiwhats.artechsolutions-ec.com/webhooks/whatsapp.php
```

Los webhooks son procesados automáticamente por la API PHP (status updates, mensajes entrantes, eventos de billing). El backend Node.js **no necesita gestionar webhooks directamente**. La información actualizada se obtiene consultando los endpoints de reportes y conversation_history.

---

## Códigos de error HTTP

| Código | Significado |
|--------|-------------|
| 200    | Éxito |
| 400    | Parámetros faltantes o inválidos |
| 401    | Credenciales inválidas o faltantes |
| 403    | Empresa inactiva o acción no permitida |
| 404    | Recurso no encontrado |
| 429    | Límite diario de mensajes alcanzado |
| 500    | Error interno del servidor |

---

## Lo que debe implementar el backend Node.js

1. **Modelo de usuarios propios** — usuarios con email/password que pertenecen a una empresa. JWT para sesiones.
2. **Middleware de empresa** — al autenticar un usuario, resolver su `company_id` y cargar las credenciales API (api_key, api_secret) para todas las peticiones a la API PHP.
3. **Proxy a la API PHP** — un servicio/helper que centralice las llamadas HTTP a la API PHP, inyectando los headers `X-API-Key` y `X-API-Secret`.
4. **Rutas REST para el frontend** — mapear las acciones de la API PHP a rutas Express limpias:
   - `GET  /api/dashboard` → `?action=dashboard`
   - `GET  /api/templates` → `?action=templates`
   - `POST /api/templates/sync` → `?action=sync_templates`
   - `POST /api/messages/send` → `?action=send_individual`
   - `POST /api/messages/bulk` → `?action=send_bulk`
   - `GET  /api/campaigns` → `?action=campaigns_list`
   - `POST /api/campaigns` → `?action=campaign_create`
   - `GET  /api/campaigns/:id` → `?action=campaign_detail&id=:id`
   - `POST /api/campaigns/:id/send` → `?action=campaign_send`
   - `GET  /api/conversations/:phone` → `?action=conversation_history&phone=:phone`
   - `GET  /api/reports/:type` → `?action=reports&type=:type` o `reports_advanced`
   - `GET  /api/company` → `?action=company_info`
   - `PUT  /api/company` → `?action=update_company`
   - `POST /api/users` → `?action=create_user`
   - `GET  /api/users` → `?action=list_users`
   - `PUT  /api/users/:id` → `?action=update_user`
   - `DELETE /api/users/:id` → `?action=delete_user`
   - `GET  /api/admin/stats` → `?action=superadmin_stats`
   - `GET  /api/admin/companies` → `?action=superadmin_companies`
   - `POST /api/admin/companies` → `?action=superadmin_create_company`
   - `POST /api/admin/companies/clone` → `?action=clone_company`
   - `GET  /api/admin/companies/:id/users` → `?action=company_users&company_id=:id`
   - `POST /api/admin/companies/:id/deactivate` → `?action=deactivate_company`
   - `DELETE /api/admin/companies/:id` → `?action=delete_company`
5. **Variables de entorno** — la URL base de la API PHP, credenciales por empresa, JWT secret.
6. **Sin acceso directo a MySQL** — toda data sale de la API PHP vía JSON.
7. **Gestionar X-User-Id** — al hacer login, guardar el `user_id` y `role` del usuario. Para los endpoints protegidos (`create_user`, `update_user`, `delete_user`, `update_company`), enviar el header `X-User-Id` con el ID del usuario logueado. El frontend no necesita saber esto; el backend lo inyecta automáticamente.

---

## 15. Sistema de Roles y Permisos

### Jerarquía de roles

| Rol | Nivel | Permisos |
|-----|-------|----------|
| `viewer` | 1 | Solo lectura: dashboard, templates, reports, company_info, list_users, conversation_history |
| `agent` | 2 | Todo lo de viewer + envío de mensajes: send_individual, send_bulk, campaigns |
| `admin` | 3 | Todo lo de agent + gestión: create/update/delete_user, update_company |

### Super-admin (empresa #1)

Los endpoints `superadmin_*`, `clone_company`, `deactivate_company` y `delete_company` están restringidos a la empresa con `id = 1`. No es un rol adicional; se valida por `company_id === 1`. El usuario con `id = 1` es el super-administrador del sistema.

### Header X-User-Id

Los endpoints que modifican usuarios o la empresa requieren identificar QUIÉN hace la operación:

```
Endpoints que requieren X-User-Id (rol admin):
  POST ?action=create_user
  POST ?action=update_user
  POST ?action=delete_user
  POST ?action=update_company

Endpoints que NO requieren X-User-Id:
  Todos los demás (login, dashboard, templates, send_*, campaigns, reports, etc.)
```

### Flujo en Node.js

```javascript
// Al hacer login, guardar user_id y role
const loginRes = await phpApi.login(user, password);
const { api_key, api_secret, user_id, role } = loginRes;

// Guardar en sesión/JWT
req.session.apiKey = api_key;
req.session.apiSecret = api_secret;
req.session.userId = user_id;
req.session.role = role;

// Middleware para endpoints protegidos por rol
function requireAdminRole(req, res, next) {
  if (req.session.role !== 'admin') {
    return res.status(403).json({ error: 'Se requiere rol admin' });
  }
  next();
}

// Ejemplo: crear usuario (protegido)
app.post('/api/users', requireAdminRole, async (req, res) => {
  const result = await phpApi.call('create_user', req.body, {
    'X-API-Key': req.session.apiKey,
    'X-API-Secret': req.session.apiSecret,
    'X-User-Id': req.session.userId  // <-- REQUERIDO
  });
  res.json(result);
});
```

### Errores de rol

```json
// Sin X-User-Id → HTTP 403
{ "error": "Se requiere header X-User-Id para esta acción." }

// Usuario no encontrado o inactivo → HTTP 403
{ "error": "Usuario no encontrado o inactivo en esta empresa." }

// Rol insuficiente → HTTP 403
{ "error": "Se requiere rol 'admin' o superior. Tu rol: 'agent'." }
```
