# API Pública — ASISMA CRM

Dos endpoints públicos (sin sesión). Todo lo demás requiere sesión iniciada.

---

## 1. Captación de Leads

```
POST {APP_URL}/api/leads
```

Recibe leads desde formularios web, landings, Meta Ads u otros sistemas.
Crea empresa (si corresponde), contacto y lead, y dispara la **asignación
en cascada inmediata**.

### Autenticación

API key en el header `X-API-KEY` (recomendado) o en el campo `api_key` del body.
Cada franquiciado tiene su **propia llave** (`empresas_franquiciados.api_key_leads`),
visible en **Comercial → API de Leads** junto a ejemplos personalizados por producto.
También funciona la llave global `API_KEY_LEADS` de config_sistema (opcional).

### Formato

`Content-Type: application/json` (recomendado) o `application/x-www-form-urlencoded`.

### Campos

| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| `rut_empresa` | string | ✔ | RUT de la empresa (dígito verificador validado; crea/reusa la ficha) |
| `razon_social` | string | ✔ | Razón social de la empresa |
| `nombre_empresa` | string | ✔ | Nombre de fantasía / comercial |
| `nombre_contacto` | string | ✔ | Nombre de la persona (alias: `nombre`) |
| `telefono` | string | ✔ | Teléfono/WhatsApp (se normaliza) |
| `correo` | string | ✔ | Email (se valida) |
| `producto_id` | int | ✔ | Producto de la landing — define vertical/marca y **hace rotar el lead entre franquiciados que venden ese producto** |
| `region_id` | int | ✔ | ID de región del cliente (tabla `regiones`) |
| `giro` | string | — | Giro comercial |
| `origen` | string | — | `web` `meta` `whatsapp_directo` `api` `referido` (default `api`) |
| `notas` | string | — | Nota inicial en la bitácora |

La vertical y la marca **no se envían**: se derivan del producto.

### Ejemplo

```bash
curl -X POST "http://asismacrm.duckdns.org/api/leads" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: asisma_lead_xxxxxxxxxxxxxxxx" \
  -d '{
    "rut_empresa": "77000111-0",
    "razon_social": "Farmacia San Pablo Ltda",
    "nombre_empresa": "Farmacia San Pablo",
    "nombre_contacto": "Roberto Fuentes",
    "telefono": "+56 9 4444 4401",
    "correo": "rfuentes@fsanpablo.cl",
    "producto_id": 72,
    "region_id": 7,
    "origen": "web",
    "notas": "Solicitó demo desde la landing"
  }'
```

### Respuestas

**201 — creado y asignado:**
```json
{ "success": true, "lead_id": 42, "asignado": true, "franquiciado_id": 2,
  "message": "Lead creado y asignado a un franquiciado." }
```

**201 — creado sin franquiciado calificado** (`asignado: false`): queda en
espera; el cron lo reintenta automáticamente cuando haya franquiciados
disponibles.

**200 — duplicado** (lead activo con mismo teléfono y vertical, o la misma
empresa con lead abierto en esa región y vertical):
```json
{ "success": true, "duplicado": true, "lead_id": 42,
  "message": "Ya existe un lead activo para este teléfono y vertical. Se registró el reingreso." }
```
El reingreso queda anotado en la bitácora del lead existente.

**Errores:** `401` API key faltante o inválida · `422` datos inválidos (campo
`errores[]`) · `500` error interno.

Log: `logs/api_leads.log`.

---

## 2. Webhook de WhatsApp (Evolution API)

```
POST {APP_URL}/api/whatsapp/webhook?t={token-único-de-la-línea}
```

Receptor de eventos de Evolution API. Procesa `messages.upsert` (mensajes
entrantes y ecos de salientes); el resto de eventos responde `200` y se ignora.

**Arquitectura**: las líneas de WhatsApp pertenecen a los **vendedores**. Cada
franquicia opera su propio servidor Evolution (Railway) y dentro de él una
instancia por vendedor. Como Evolution configura el webhook por instancia,
**cada línea tiene una URL única** diferenciada por el parámetro `?t=` (token
generado al registrar la instancia; copiable desde WhatsApp → Instancias).

- La instancia se resuelve por el token `?t=` de la URL; como fallback, por el
  campo `instance` del payload. Debe estar registrada y activa en
  **WhatsApp → Instancias** (si no, se ignora).
- El lead se resuelve por los últimos 8 dígitos del teléfono, priorizando:
  lead ya vinculado a la instancia → lead del **vendedor dueño de la línea** →
  lead del franquiciado → lead activo más reciente. Sin lead asociado, el
  mensaje se descarta.
- Se ignoran chats grupales (`@g.us`) y estados.
- Adjuntos: usa el `base64` del payload; si no viene, lo recupera con
  `/chat/getBase64FromMediaMessage`. Se guardan en `storage/whatsapp_media/`
  y se sirven autenticados vía `GET /whatsapp/media?id={mensaje}`.
- Deduplicación por `message_id` + instancia (los reintentos del webhook no duplican).
- Cada mensaje queda en `whatsapp_mensajes` y como actividad `whatsapp` en la
  bitácora del lead.

Log: `logs/whatsapp_webhook.log`.

---

## 3. Notificación saliente a la API de facturación del franquiciado

Cuando un lead pasa a **Ganado**, el CRM notifica a la URL configurada en la
empresa franquiciada (`api_facturacion_url`), con header
`X-API-KEY: {api_key_facturacion}`:

```json
POST {api_facturacion_url}
{
  "evento": "lead_ganado",
  "lead_id": 42,
  "franquiciado_id": 2,
  "fecha": "2026-07-06 18:30:00",
  "cliente": {
    "rut": "77000111-2",
    "razon_social": "Farmacia San Pablo Ltda",
    "telefono": "+56944444401",
    "correo": "rfuentes@fsanpablo.cl"
  },
  "cotizacion": { "id": 7, "total": 89250.0, "moneda": "CLP", "estado": "aceptada" }
}
```

Se considera exitosa cualquier respuesta HTTP 2xx. El resultado (éxito o fallo)
queda registrado en la bitácora del lead.
