# Contrato de integración con n8n — WebChatV2 El frontend hace un `POST` al **webhook de n8n** con el mensaje del usuario y el historial, y espera una respuesta JSON con el texto del asistente y, opcionalmente, KeyNotes (resumen / To-Do) que se insertan automáticamente en el panel derecho y que el usuario puede editar después. ## 1. Configuración de la URL La URL del webhook se resuelve en **tiempo de ejecución** (no se "hornea" al compilar), para que puedas cambiarla sin reconstruir la imagen Docker: 1. `public/config.js` → define `window.APP_CONFIG.N8N_WEBHOOK_URL` (prioridad alta). 2. Si no existe, se usa la variable de entorno `VITE_N8N_WEBHOOK_URL` (definida al compilar). En Docker, edita el archivo `config.js` servido por nginx (o monta un volumen sobre `/usr/share/nginx/html/config.js`) y recarga la página. ## 2. Request — `POST {N8N_WEBHOOK_URL}` `Content-Type: application/json` ```json { "themeId": "theme_abc123", "subThemeId": "sub_def456", "themeTitle": "Tema Principal", "subThemeTitle": "Subtema 1", "history": [ { "role": "user", "content": "Hola" }, { "role": "assistant", "content": "¿En qué puedo ayudarte?" } ], "message": "Explícame cómo funciona esto" } ``` | Campo | Tipo | Descripción | | --------------- | -------- | ------------------------------------------------------------------ | | `themeId` | string | ID del MainTheme activo. Útil para guardar contexto en n8n/DB. | | `subThemeId` | string | ID del SubTheme activo (la conversación actual). | | `themeTitle` | string | Título del tema (legible). | | `subThemeTitle` | string | Título del subtema (legible). | | `history` | array | Mensajes previos `{role, content}` (orden cronológico). | | `message` | string | Texto que el usuario acaba de escribir (NO está incluido en history).| `role` es `"user"` | `"assistant"`. ## 3. Response — `200 OK` (JSON) ```json { "reply": "Respuesta en **Markdown** del asistente…", "keyNotes": [ { "type": "summary", "title": "Resumen de la conversación", "content": "Puntos clave tratados…" }, { "type": "todo", "title": "Tareas pendientes", "items": [ { "text": "Revisar la documentación", "done": false }, { "text": "Configurar el webhook", "done": true } ] } ] } ``` ### Reglas - `reply` (**obligatorio**, `string`): texto de respuesta. Se renderiza como **Markdown** (soporta GFM: listas, tablas, bloques de código, etc.). - `keyNotes` (**opcional**, `array`): notas que se añaden al SubTheme activo con `source: "n8n"`. El usuario puede editarlas/eliminarlas libremente. ### Tipos de KeyNote | `type` | Requiere | | --------- | -------------------------------------------------------- | | `summary` | `title` + `content` (string, Markdown plano) | | `todo` | `title` + `items[]` con `{ text: string, done?: boolean }` | | `free` | `title` + `content` | `done` es opcional y por defecto `false`. ## 4. Errores y timeouts - Cualquier status HTTP fuera del rango 2xx se trata como error y se marca el mensaje como `status: "error"`. El usuario puede pulsar **Reintentar**. - Timeout por defecto: **120 s**. La solicitud puede cancelarse con el botón **Detener** (usa `AbortController`), lo que aborta el fetch hacia n8n. ## 5. Ejemplo de workflow n8n (orientativo) ``` Webhook (POST /webhook/chat) → Set (extraer body.message, body.history, body.themeTitle…) → [tu LLM / lógica] → Set (construir JSON { reply, keyNotes }) → Respond to Webhook (JSON, 200) ``` > En n8n usa el nodo **Respond to Webhook** con `Respond With: JSON` y en `Response Body` > pega el objeto `{ "reply": ..., "keyNotes": [...] }`. ## 6. Evolución futura (streaming / persistencia en DB) - **Streaming:** el contrato queda preparado para añadir SSE (`text/event-stream`) más adelante sin romper la UI (el indicador "escribiendo…" ya existe). - **Persistencia:** el estado vive hoy en `localStorage`. Para sincronizar con una DB vía n8n, añade endpoints `GET/POST /webhook/state` y reemplaza el adapter en `src/store/persistence.ts` (punto único de intercambio).