Documentación de la API
Endpoints REST expuestos por n8n para el portal de gestión Yurest. Base URL: https://n8n.yurest.dev/webhook
Introducción
Esta API se expone vía workflows de n8n y persiste en Postgres self-host (data.n8n.yurest.dev, ex-Supabase). Todos los endpoints comparten el prefijo:
https://n8n.yurest.dev/webhook
El frontend usa YurestConfig.apiFetch(url, opts), que añade automáticamente la cabecera X-Session-Token (vía getAuthHeaders()), deduplica GETs idénticos en vuelo, aplica un timeout de 30 s (override con timeoutMs) y maneja sesiones expiradas (401/403 → limpia sesión y redirige a login, salvo skipAuthLogout:true).
200 OK con un objeto JSON. Operaciones de escritura devuelven al menos { success: boolean } y, donde aplica, datos de diagnóstico (filas afectadas, errores específicos).
Autenticación
Authorization: Basic fue eliminada del cliente y rotada. Ya no se envía. Todos los webhooks validan ahora un token de sesión firmado (HMAC-SHA256).
El login lo emite el workflow WF16 (auth/login). Cada petición del portal lo manda en la cabecera X-Session-Token, que añade automáticamente YurestConfig.apiFetch() a través de getAuthHeaders(). Cada workflow valida el token server-side (el «Auth Gate HMAC» inline, ~51 workflows migrados).
X-Session-Token: <userId>|<exp>.<hmac>
Content-Type: application/json
Formato del token
| Parte | Contenido |
|---|---|
userId | ID del usuario autenticado (tabla usuarios). |
exp | Epoch de expiración. TTL 30 días si «recordar», 8 h si no. |
hmac | HMAC_SHA256(userId|exp, SECRET). El secreto vive solo en n8n. |
localStorage.yurest_backend === 'laravel', getAuthHeaders() manda en su lugar Authorization: Bearer <token> (Sanctum) + Accept: application/json y reescribe la URL al backend nuevo (api.yurest.com). Los endpoints sin mapping degradan a n8n. Por defecto el portal sigue contra n8n con X-Session-Token.
Si la sesión expira, cualquier endpoint responde 401/403; apiFetch limpia la sesión y redirige a login.html. Las peticiones de fondo pasan { skipAuthLogout:true } para no expulsar al usuario ante un 401 best-effort.
Endpoints públicos (sin sesión)
El cliente final no tiene cuenta; estos endpoints se autorizan por token aleatorio incrustado en el enlace del email, no por sesión.
| Endpoint | Autorización | Por qué es público |
|---|---|---|
auth/login | usuario + contraseña | Emite el token de sesión. |
responderSolicitud | access_token | El cliente rellena la ficha desde el email. |
completarFicha?t=<token> | token de solicitud | Precarga datos del formulario del cliente. |
oferta-publica?t=<token> | token de oferta | Vista pública de la oferta (configurador). |
pago/iniciar | token de oferta | El cliente paga desde la oferta pública (Redsys). |
oferta-cambios (POST) | token de oferta | Telemetría de cambios del cliente en la oferta (fire-and-forget). |
Errores y diagnóstico
| Status | Significado | Cuándo |
|---|---|---|
200 | OK · revisa body.success | Casi todas las respuestas (incluso fallos lógicos vienen 200). |
401 / 403 | Sesión expirada | Frontend redirige a login.html. |
500 | Error interno del workflow | Excepción no controlada en n8n. |
eliminarFicha, grabadoA3) devuelven además { success, affected/ficha_count/sol_count, errores: [] } para detectar UPDATEs sin efecto (RLS bloqueando, ID inexistente, etc.).
CORS
Todos los endpoints aceptan peticiones desde cualquier origen y responden con los siguientes headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
🔑 Auth y usuarios
Workflow WF16. Valida credenciales contra la tabla usuarios y, si son correctas, emite el token de sesión firmado (HMAC) que el resto de la API exige.
Body
{ "username": "consultor@yurest.com", "password": "..." }
Respuesta 200 (éxito)
{
"success": true,
"user": {
"id": "uuid",
"username": "consultor@yurest.com",
"nombre": "Ana García",
"email": "consultor@yurest.com",
"rol": "admin | user",
"token": "<userId>|<exp>.<hmac>",
"permisos": { "read": [...], "write": [...], "delete": [...] }
}
}
success && user.username && user.rol. Credenciales inválidas → mensaje neutro (no distingue usuario inexistente de contraseña incorrecta).Verifica que la sesión sigue viva (chequeo periódico del portal). Un 401/403 fuerza logout.
Gestión de usuarios y roles (Administración → admin.html). auth/usuarios lista/crea/edita/borra usuarios y sus permisos; auth/roles gestiona los roles base. Acciones vía action en el body. El rol se calcula como base + deltas granulares { read, write, delete }.
| Endpoint | Uso |
|---|---|
auth/usuarios | CRUD de usuarios + permisos (create/update/password/delete). |
auth/roles | Catálogo de roles base y sus permisos por defecto. |
auth/usuarios/historial | GET del audit log de acciones sobre usuarios (pestaña «Historial»). Tabla usuarios_historial. |
📋 Fichas
Workflow 04-fichas-alta. Devuelve la lista completa de fichas (con su cs_estado del Kanban CS). Alias en config.js: ENDPOINTS.login · ENDPOINTS.altas.
Respuesta 200
{
"clientes": [
{
"id": "uuid",
"denominacion": "Test SL",
"cif": "B40597437",
"email": "contacto@test.es",
"tipo_cliente": "lite | planes | corporate",
"estado": "rellenado | completada",
"cs_estado": "en_implementacion | post_primer_mes | ...",
"grabado_a3": false,
"grabado_a3_at": null,
"introducido_yurest": false,
"promocion_id": null,
"fecha_solicitud": "2026-04-17T...",
"fecha_rellenado": "2026-04-17T...",
"fecha_completado": "2026-04-17T...",
"sepa_mandato": { ... },
"adjuntos": [ ... ],
"paquetes_carrito": { ... },
"deleted_at": null
// ... resto de columnas
}
]
}
Filtrado backend
El workflow excluye filas con deleted_at != NULL. Devuelve todas las columnas, incluidas las vacías como "".
📋 Fichas
Workflow 04-fichas-alta. Crea una ficha nueva (sin id) o actualiza una existente (con id). En modo edición solo actualiza los campos enviados — no destruye los demás.
Body (todos opcionales en edición)
| Campo | Tipo | Notas |
|---|---|---|
id | uuid | Si presente → UPDATE; ausente → INSERT |
Nombre Sociedad | string | Mapea a denominacion |
CIF/NIF | string | Mapea a cif |
Email · Email Factura · Email CC | string | |
Tipo Cliente | string | lite · planes · corporate |
Estado | string | "rellenado" cuando consultor completa |
JP * · Firmante * · TPV * | strings | Bloques de datos |
Lite · Distribuidor | "Sí" / "" | Booleanos como string |
Adjuntos | array | PDF/JPG/PNG en base64 |
SEPA | object | Mandato firmado (datos + firma_base64) |
paquetes_carrito | object | Snapshot del carrito (módulos, precios, periodos) |
locales | array | Locales de la ficha (insertados aparte) |
Respuesta 200 (éxito)
{ "id": "uuid-de-la-ficha", "success": true }
Respuesta 200 (error en INSERT Locales)
{ "error": "..." }
El nodo INSERT Locales tiene continueOnFail: true, así que un fallo en locales no rompe el INSERT/UPDATE de la ficha principal pero sí aparece en el body.
Workflow 12-completar-ficha. Devuelve los datos de una ficha o solicitud para precargar el formulario. Acepta tres formas de identificación.
Query params
| Param | Tipo | Uso |
|---|---|---|
t | string (16-128 chars) | Token aleatorio público (preferente) |
id | uuid o número | UUID de ficha o ID-Solicitud numérico (uso interno) |
Respuesta 200 (encontrado)
{
"found": true,
"id": "uuid",
"ID Solicitud": 5,
"Nombre Sociedad": "Test SL",
"Email": "contacto@test.es",
"Tipo Cliente": "planes",
"SEPA": { "iban": "ES...", "deudor": {...}, "firma_base64": "..." },
// ... resto de campos
}
Respuesta 200 (no encontrado)
{ "found": false, "error": "Solicitud no encontrada" }
Workflow 10-eliminar. Soft-delete (UPDATE deleted_at = NOW()) en fichas_alta y solicitudes. Como los UUIDs son únicos entre tablas, sólo afecta a la que contiene el ID.
Body
{
"action": "delete",
"entity": "ficha" | "solicitud",
"id": "uuid",
"row_number": "uuid" // alias por compat
}
Respuesta 200
{
"success": true,
"entity": "ficha",
"id": "uuid",
"ficha_count": 1,
"sol_count": 0,
"errores": []
}
errores (problemas Supabase) o si ficha_count + sol_count == 0 → el id ya no existía.Acciones puntuales sobre una ficha ya creada (no re-guardan la ficha entera). Todas reciben { ficha_id } y devuelven { ok, ... }.
| Path | Workflow | Efecto |
|---|---|---|
/ficha/notificar-completa | WF19 | Al completar la ficha: email Drive al cliente + tarea Asana + email integraciones. Antes vivía en WF11. |
/ficha/introducir-yurest | WF64 | Flag «Introducida en Yurest» (idempotente). 1ª vez envía aviso. → { ok, introducido_yurest, introducido_yurest_at }. |
/ficha/marcar-ganado-hl | WF65 | { ficha_id, marcar_ganado:true } → mueve la oportunidad HighLevel a won + «Ganados Planes» y escribe MRR/ARR/nº locales/periodicidad. |
/contrato-enviar | WF84 | Envía el contrato a firmar (efirma) → { ok, id_operacion, destinatarios }. Con { preview:true } devuelve el HTML sin enviar. |
/fichas-adjuntos-upload | WF35 | { nombre, tipo, data(dataURL), ficha_id? } → sube al bucket Storage. → { storage_path, signed_url, size }. |
/sepa/solicitar-firma | WF70 | { ficha_id, mandato_id, email } → envía al cliente enlace a firmar-sepa.html (token HMAC). |
/fichas/importar-sheet | WF54 | { sheetUrl } → importa ficha desde Google Sheets. → { ok, ficha, avisos }. |
Modal «Ver cliente» (clientes.html): notas internas, movimiento en el Kanban CS y asignación de promoción.
| Path | Método | Uso |
|---|---|---|
/cliente-notas | GET ?ficha_id= / POST | Notas internas firmadas con el email del JWT. GET → { notas:[{id,autor_email,texto,created_at}] }; POST { ficha_id, texto }. |
/cliente-notas-editar · /cliente-notas-borrar | POST | Editar / borrar una nota. |
/cs-estado | POST | Mueve el cliente entre columnas del Kanban CS. Actualiza cs_estado + cs_estado_historial. |
/ficha-asignar-promo | POST | { ficha_id, promo_id, promo_turno } → patch de promocion_id/promocion_turno/promo_asignada sin re-guardar la ficha. |
📨 Solicitudes
Workflow 08-solicitudes. Inserta solicitud en estado pendiente y envía email al cliente con la URL del formulario.
Body
{
"ID Solicitud": 5,
"access_token": "32-chars-uuid-sin-guiones",
"Nombre Sociedad": "Empresa SL",
"Nombre": "Ana García",
"Email": "ana@empresa.com",
"Consultor": "Pedro Martin",
"Tipo Cliente": "planes",
"Fecha": "2026-04-17",
"Estado": "Pendiente",
"email_to": "ana@empresa.com",
"email_subject": "Solicitud de creación de ficha de alta",
"email_body": "Hola Ana, te mando este formulario...",
"form_url": "https://.../solicitud.html?t=..."
}
Si access_token no viene, el workflow lo genera con crypto.randomUUID().
Respuesta 200
{ "id": "uuid", "success": true }
Listados y utilidades del flujo de solicitudes.
| Path | Método | Uso |
|---|---|---|
/webhook/1757fdcc-… (listaSolicitudes) | GET | WF08. Solicitudes NO completadas → { solicitudes:[{id,access_token,"ID Solicitud","Nombre Sociedad",Email,Estado,Fecha}] }. |
/webhook/fa16b994-… (listaRellenado) | GET | WF09. Solicitudes con estado=completada (esperan al consultor) → { items:[{id,"Nombre Sociedad",Email,Consultor}] }. |
/solicitud-prellenar-cliente | POST | WF38. El consultor pre-rellena todo menos SEPA: { access_token, datos_parciales } → merge JSONB sobre solicitudes.datos respetando SEPA. El cliente solo firma. |
/webhook/a2b1b1d6-… (eliminarSolicitud) | POST | Mismo webhook que eliminarFicha (soft-delete). { action:'delete', entity:'solicitud', id }. |
Workflow 11-auxiliares. El cliente envía sus datos desde solicitud.html. Crea la ficha, carpeta Drive, marca solicitud completada y dispara emails + tarea Asana si TPV no integrado.
Body (todos los datos del formulario)
{
"ID Solicitud": 5,
"access_token": "...",
"Estado": "Rellenado cliente",
"Nombre Sociedad": "...", "CIF/NIF": "...",
"JP Nombre": "...", "Firmante DNI": "...",
"TPV": "Revo",
"TPV No Integrado": "Sí", // si checkbox marcado
"TPV NI Nombre": "...", "TPV NI Contacto": "...", "TPV NI Email": "...",
"SEPA": {
"iban": "ES91...",
"swift": "CAIXESBB",
"deudor": { "nombre": "...", "direccion": "...", ... },
"tipo_pago": "recurrente",
"firma_base64": "data:image/png;base64,...",
"firmado_at": "2026-04-17T..."
}
}
Efectos
- INSERT en
fichas_altaconestado=completada+ SEPA persistido. - Crea carpeta en Google Drive y guarda
carpeta_driveen la ficha. - Email al cliente con enlace al Drive.
- Email al consultor avisando.
- UPDATE solicitudes:
estado=completado+ficha_id(dispara trigger que copiafecha_solicitud). - Si TPV no integrado: tarea en Asana (proyecto Integraciones
1207920061546505) + email a soporte/a.jareno/luis.
Respuesta 200
{ "success": true }
💼 Ofertas y pagos
Workflow 30-ofertas. Ofertas generadas por el configurador (departamento Consultor). GET lista con filtros; ?id=<uuid> devuelve una sola.
POST — action
| action | Efecto |
|---|---|
create · update | Alta/edición de la oferta. |
archivar · reactivar | Estado de archivo. |
cambiar_estado | enviada (email) / ganada (ficha arrancada) / etc. |
vincular_ficha | Asocia la oferta a una ficha de alta. |
PRO-YYYY-<uuid8> (preview, sin numeración fiscal); pedidos hardware (WF21) → PRO-AAAA-NNNN correlativa reservada por RPC.| Path | Auth | Uso |
|---|---|---|
/oferta-publica?t=<token> | Público (token) | WF36. Vista pública de la oferta (configurador.html?t=). |
/oferta-enviar-comercial | X-Session-Token | WF37. { oferta_id, portal_base_url } → email al cliente con link, enviado desde la credencial SMTP/Gmail del consultor dueño (fallback a soporte). La ruta antigua /oferta-enviar (WF36) sigue como fallback. |
/oferta-cambios | POST público · GET HMAC | WF80. POST {token, snapshot} (telemetría fire-and-forget con debounce desde el configurador); GET historial por ?oferta_id=N o counts agrupados desde ofertas.html. |
Workflow 55-pasarela (Redsys/BBVA). El cliente, desde la oferta pública, pulsa «Pagar».
POST /pago/iniciar
// Request
{ "token": "<token-oferta>" }
// Respuesta — el front auto-submitea un form a Redsys
{
"ok": true,
"redirect": {
"url": "https://sis.redsys.es/...",
"Ds_SignatureVersion": "HMAC_SHA256_V1",
"Ds_MerchantParameters": "<base64>",
"Ds_Signature": "<firma>"
}
}
El importe es final_amount × 1,21 (IVA). La firma Redsys (HMAC_SHA256_V1, 3DES en JS puro) y el inserto en payments se hacen server-side. La notificación S2S y las páginas OK/KO las sirve el propio workflow. /pago/enviar-ficha-link reenvía al email del cliente el enlace de su solicitud («Continuar más tarde»).
Ampliaciones contractuales sobre clientes existentes (módulos o locales nuevos). GET lista; POST crea uno en estado pendiente. La aplicación real sobre fichas/locales/SEPA la hace un workflow al confirmar el escalado.
📒 Contabilidad
Workflow 13-grabado-a3. Toggle del flag grabado_a3 en una ficha. Trigger SQL setea grabado_a3_at al pasar de FALSE a TRUE.
Body
{ "id": "uuid", "grabado_a3": true | false }
Respuesta 200
{
"success": true,
"id": "uuid",
"grabado_a3": true,
"affected": 1,
"errores": []
}
Workflow 39. Lectura de Azure SQL (yurestazure / Info_PowerBI). La página pinta una tabla genérica con auto-detección de columnas; la query se ajusta en n8n (la página no envía SQL).
Respuesta 200
{ "ok": true, "total": 120, "columnas": [...], "rows": [...], "generated_at": "ISO" }
📁 Proyectos
Workflow 01-proyectos-crud. CRUD de proyectos de implementación.
| Método | Body | Respuesta |
|---|---|---|
| GET | — | { proyectos: [...] } |
| POST | { proyecto: {...} } | { data: {...}, success: true } |
| PUT | { proyecto: { id, ...campos } } | id devuelto |
| DELETE | { id: "uuid" } | Soft-delete |
El POST/PUT acepta asanaProjectId y asanaProjectUrl para vincular un tablero Asana.
Workflow 02-proyectos-tareas. Actualiza una tarea/sección dentro de un proyecto (toggle completada, edición, etc.).
Body
{
"proyectoId": "uuid",
"seccionNombre": "Puesta en Marcha / Finalización",
"tarea": { "id": "...", "completada": true, ... }
}
Respuesta 200
{ "success": true }
Mover una tarea entre secciones (drag & drop kanban) o eliminarla.
Body — mover
{ "proyectoId": "uuid", "tareaId": "...", "seccionOrigen": "...", "seccionDestino": "..." }
Hilo cliente ↔ staff e historial del proyecto (WF01).
| Path | Método | Uso |
|---|---|---|
/proyectos/mensajes | GET ?proyectoId= | → { ok, mensajes:[…] } (asc). El mismo hilo que ve el cliente en espacio-cliente.html. |
/proyectos/mensajes | POST | { proyectoId, texto } (máx 4000). El backend fija autor_tipo='staff' y el nombre a partir del token. |
/proyectos/historial | GET | Timeline de acciones del proyecto. |
Proxy e importadores de Asana (las credenciales viven solo en n8n).
| Path | Uso |
|---|---|
/asana/tasks?projectId= · /asana/task/stories?taskId= | WF07. Proxy de lectura: tareas de un proyecto e historias (comentarios) de una tarea. |
/proyectos/asana-import | WF81. Importa proyectos arrancados en Asana. POST { op:'tasks'|'detalles' }. |
/asana/project-sections | Lista secciones de un proyecto (pestaña Formación grupal: el CS escoge qué sección representa al cliente). |
🎓 Promociones
Tandas de implementación (Customer Success): 16 plazas (8 mañana + 8 tarde). Doble proyecto Asana por promoción.
| Path | Uso |
|---|---|
/promociones | Vista con la ocupación ya calculada. La creación es manual. |
/promociones/asana?project=<gid> | WF56. Lee nombre + secciones en vivo → { ok, gid, nombre, total, ocupados, libres }. |
/promociones/asana/ocupar | WF63. { project_gid, title } → renombra la 1ª sección libre → { ok, section_gid?, nuevo_titulo?, libres_restantes? }. |
/promociones/asana-import-tareas | WF83. { promocion_id, turno } → importa tareas por sección/turno → { ok, promo, actualizados, huerfanas, sinSeccion }. |
📅 Sesiones y calendario
Workflow 07-calendar-asana. Google Calendar del implementador.
| Path | Uso |
|---|---|
/calendar/event (POST) | Crea/actualiza un evento (sesión de implementación). { summary, start, end, attendees[] }. El reagendado actualiza por googleEventId. |
/calendar/disponibilidad | Lee el calendario en un rango para detectar solapes al agendar. |
Panel de sesiones de implementación (WF52).
| Path | Uso |
|---|---|
/panel/sesion-resumen | Dado un evento ya finalizado, lee las notas de Gemini → { show, resumen, duracionMin }. |
/panel/enviar | Envía el email de resumen (Gmail real): { implementador, correosInvitados, titulo, html }. |
🖥️ Hardware
| Path | Uso |
|---|---|
/hardware/pedidos | Pedidos por proyecto: ciclo solicitada → proforma → pago → lista_envío. Consumido desde proyecto.Hardware, contabilidad.Proformas y Soporte envíos. |
/hardware/stock | Catálogo del almacén (Soporte): stock actual, mínimo, precios y movimientos. Acciones: create, update, archivar, reactivar, movimiento. |
🧩 Producto
Desarrollos a medida por cliente (reemplaza el Excel de Producto).
| Path | Uso |
|---|---|
/presupuestos | CRUD: quién paga, estado de aprobación y de entrega. |
/presupuestos-asana | Importa tareas de la sección «Pendiente de presupuesto» (Back Clientes). POST { action:'import'|'refresh', asana_gid }. |
/presupuestos-asana-attach | Adjunta el PDF generado a la tarea Asana de origen (base64 → multipart). |
/presupuestos/enviar-cliente | WF22. Envía el PDF por email al cliente (Gmail soporte@). |
Workflow WF66. Horas contratadas vs consumidas por cliente. Las contratadas se registran como compras (cantidad + PDF pago + PDF factura); las consumidas se derivan de los presupuestos aceptados/pagados.
| Path | Uso |
|---|---|
GET /bolsa-horas | → { compras:[…], asignaciones:[…] }. |
/bolsa-horas-crear | Sube ambos PDFs e inserta la compra. |
/bolsa-horas-editar · -archivar · -reemplazar-pdf | Editar horas/fecha, archivar, reemplazar un PDF. |
/bolsa-horas/asignacion · -borrar | Upsert / borra una asignación bolsa↔presupuesto. |
Workflow WF57. Lee en vivo el proyecto Asana «Sugerencias optimizaciones clientes» (tareas + secciones + etiquetas + campos, normalizado y paginado).
| Path | Uso |
|---|---|
GET /triaje/asana | Sugerencias normalizadas. |
/triaje/asana/edit | Edita una sugerencia → escribe en Asana (PUT name/notes + add/removeTag de módulo). |
/triaje/asana/orden | Orden de prioridad manual compartido (mapa módulo→[gids], Data Table triaje_orden). |
Base de conocimiento de sprints (gpt-4o). Se pega la URL de un proyecto Asana, se elige un campo enum y sus valores, y se genera un documento markdown de las funcionalidades del sprint.
| Path | Uso |
|---|---|
POST /documento-ia/analizar | { asana_url } → { proyecto, campos[], tareas_completadas }. |
POST /documento-ia/generar | { asana_gid, sprint_nombre, campo_gid, campo_nombre, valores[] } → { documento }. |
GET /documento-ia | → { documentos:[…] } (vivos). /borrar = soft delete; /editar. |
POST /documento-ia/preguntar | { pregunta } → { respuesta(markdown), sprints_citados[] }. |
Workflow WF73. Newsletter de sprint (/updates). Analiza un proyecto Asana, reescribe títulos con IA y arma el email.
| Path | Uso |
|---|---|
/newsletter/analizar · /vincular | Detecta proyecto/sección/campos; vincula la newsletter. |
GET /newsletter · /cargar · /guardar | Lista / carga (merge en front) / guarda { titulo, intro, items }. |
/newsletter/generar | { id, task_gids[] } → { items:[{task_gid, titulo_reescrito, beneficio}] }. |
/newsletter/enviar-prueba · /borrar · /asignar-tipo | Envío de prueba, soft delete, escribe el campo «Tipo» en Asana. |
🤝 Customer Success
Integración HighLevel (CRM). Credenciales (LOCATION_ID + TOKEN) solo en n8n.
| Path | Uso |
|---|---|
/highlevel/contactos | WF40. Listado de contactos. Filtros server-side: ?limit(1-500), ?cursor("ts,id"), ?q, ?tag, ?origen. |
/highlevel/contacto-notas · /tags | Notas del CRM de un contacto bajo demanda; catálogo de tags del location. |
/highlevel/clientes-ganados | WF49. Snapshot de oportunidades ganadas (3 pipelines) enriquecidas con contacto/business. Formato { ok, total, columnas, rows, generated_at }. |
/highlevel/clientes/refresh | WF50. Refresca el snapshot on-demand (cron 6:30 UTC). |
/highlevel/prod-sin-crm | WF51. Clientes vivos en producto SIN oportunidad ganada (match prod_id↔id_bbdd, fallback CIF). |
/highlevel/oportunidades-abiertas · /contacto-detalle | WF67. Picker de oferta desde oportunidad abierta. |
Workflow WF76. Clientes Free 3 meses. GET lista clientes_free_3m; /toggle persiste Free(0)/Lite(1); /refresh re-sincroniza contra HighLevel sin esperar al cron de las 6.
Resumen del día para el dashboard del home. GET sin parámetros → { fecha, ofertas, fichas, bajas, escalados } (contadores incrementales de home_kpis_diarios, mantenidos por triggers AFTER INSERT).
🛟 Soporte
| Path | Uso |
|---|---|
/zendesk/tickets-heatmap | Mapa de calor: ?from=YYYY-MM-DD&to=YYYY-MM-DD → matriz [día 0-6][hora 0-23]. |
/zendesk/tickets-heatmap-ia | Igual pero solo tickets aún en el agente IA (asunto «Conversation with»): cobertura del bot. |
/zendesk/resumen-semanal | Resumen ejecutivo IA de la semana. ?week=actual|anterior o ?from&to. |
/zendesk/resumen-mensual | WF29. Análisis profundo (gpt-4o) con cache por (año, mes). ?refresh=1. |
Asociación cliente ↔ organización Zendesk (modal «Ver cliente» → Tickets) y registro de errores.
| Path | Uso |
|---|---|
/zendesk/orgs | WF41. Lista organizaciones con filtro server-side (nombre/external_id/details/notes). |
/cliente/zendesk-link | WF42. PATCH zendesk_org_id (o NULL). 409 si la org ya está asociada a otro cliente. |
/zendesk/tickets-cliente | WF43. Tickets de la org asociada (ventana default 180d). |
/registro-error/org-users · /registro-error | WF67. Crea ticket Zendesk + tarea Asana enlazados → { ok, ticket_id, ticket_url, asana_gid, asana_url }. |
Snapshots diarios (06:00) + refresco manual. Cruce Zendesk × Asana.
| Path | Uso |
|---|---|
GET /incidencias-dev/estado | Último snapshot de incidencias DEV. |
POST /incidencias-dev/rebuild | Reconstruye el snapshot. |
/integraciones-tpv/estado · /rebuild | Gemelo para integraciones TPV (filtro por proveedor). |
Pestaña Formación · proyecto Asana asociado al cliente.
| Path | Uso |
|---|---|
/cliente/asana-formacion-link | WF44. { cliente_id, asana_url } → extrae GID, verifica el proyecto y PATCH asana_formacion_gid. null desvincula. |
/asana/formacion-tasks?project_gid= | WF45. → { project, counts:{done,pending,total}, tasks:[…] }. |
Mini-app «Churn técnico» (iframe). Webhooks autenticados por el Auth Gate HMAC: churnClientes, churnResumenGenerar, churnNiveles, churnResumenBuscar, churnTickets.
Notificaciones automáticas de Integraciones: configuración, grupos de destinatarios e historial de envíos.
📦 Producto Yurest (snapshot prod)
Uso del producto Yurest cacheado en Supabase (prod_clientes_snapshot) para evitar la query pesada al MySQL de producción en cada apertura.
| Path | Uso |
|---|---|
GET /prod/clientes | WF47. Lee el snapshot → { ok, total, refreshed_at, clientes:[…] }. |
POST /prod/clientes/refresh | WF48. Reejecuta la query MySQL (cron 6am UTC). Tarda 5-15s → { ok, count, refreshed_at }. |
GET /prod/clientes/nombres | Lista ligera id+nombre+nombre_comercial de TODA la tabla (para casar nombre HL → id cliente). |
⚙️ Administración
Workflow WF91. «Mis contraseñas»: gestor de credenciales de portales externos. El servidor resuelve userId y rol del token; un no-admin solo ve/edita las suyas, el admin todas.
| Path | Uso |
|---|---|
GET /admin/passwords | Lista propia (o ?scope=all admin). Nunca devuelve la contraseña, solo metadatos. |
POST /admin/passwords | { action:'save'|'delete', entry:{…} }. La contraseña se cifra en BD (pgcrypto) vía RPC; la clave la pone el workflow, nunca viaja al cliente. |
POST /admin/passwords/reveal | { id } → plano (dueño|admin) y audita en admin_passwords_audit. |
Workflow WF92. «Mi equipo»: inventario por persona. Jerarquía tipo(sección) → catálogo(modelo, coste solo-admin) → unidad(física asignada a un usuario).
| Método | Uso |
|---|---|
GET ?what=tipos|catalogo|unidades[&scope=all] | unidades: propias sin coste; admin+scope=all → todas + coste + inventario económico agregado. |
| POST | { what:'tipo'|'catalogo'|'unidad', action:'save'|'delete', payload:{…} }. Escrituras solo admin. |
Audit log.
| Path | Uso |
|---|---|
/historial | Historial de acciones por ficha. |
/historial/global | Feed unificado (usuarios + fichas + proyectos) para Logs de Administración (solo admin). Query: ?limit&offset&fuente&desde&hasta&q → { historial:[…], hasMore }. Vista v_historial_global. |
📉 Bajas
Workflow 05-bajas. Gestión de bajas de clientes (registro, edición de campos individuales y borrado).
| Endpoint | Operación |
|---|---|
bajas | GET listado |
bajaCliente · bajaLocal · bajaModulos · bajaEditar | POST campos específicos (mismo webhook con action) |
bajaBorrar | POST borra una baja |
📊 Distribución
Workflow 06-distribucion. Asigna un implementador a una ficha.
Body
{ "fichaId": "uuid", "implementador": "Carlos Aparicio" }
Workflow WF78. Distribuidores potenciales: cuando el consultor rellena el TPV de un local y su email de contacto, el workflow saca el dominio y, si no consta como distribuidor conocido, avisa a Pedro Martín y registra la fila.
| Path | Uso |
|---|---|
GET /distribuidor/detectados | → { ok, rows:[{ dominio, email_origen, contacto_nombre, tpv_nombre, ficha_id, cliente_nombre, local_nombre, estado, avisado_at }] }. |
POST /distribuidor/detectados-estado | { dominio, estado } (nuevo|contactado|descartado|alta) → PATCH de la fila. |
📁 Drive
Workflow 11-auxiliares. getDrive devuelve la URL de la carpeta Drive de una ficha; docsSubidos consulta si hay documentos subidos.
Query
?id=<ficha_uuid>
Apéndice A · Tipos de datos
Objeto SEPA
{
"referencia": "YUREST-5",
"acreedor": {
"identificador": "ES##ZZZB40597437",
"nombre": "Yurest Solutions Sociedad Limitada",
"direccion": "Calle Transits, 6",
"cp": "46002", "poblacion": "Valencia", "provincia": "Valencia",
"pais": "España"
},
"deudor": {
"nombre": "...", "direccion": "...",
"cp": "...", "poblacion": "...", "provincia": "...", "pais": "..."
},
"swift": "CAIXESBB",
"iban": "ES9121000418450200051332",
"tipo_pago": "recurrente" | "unico",
"fecha_firma": "2026-04-17",
"localidad": "Valencia",
"firma_base64": "data:image/png;base64,...",
"firmado_at": "2026-04-17T14:33:33.152Z"
}
Objeto Adjunto
{
"id": "adj_xxx",
"nombre": "contrato.pdf",
"tipo": "application/pdf" | "image/png" | "image/jpeg",
"size": 12345,
"data": "data:application/pdf;base64,...",
"fecha": "2026-04-17T..."
}
Objeto paquetes_carrito
{
"items": [{ "id": "plan_pro", "precio": 1580 }],
"descuentos": { "cart": 0, "planes_setup": 0, "planes_recur": 0 },
"periodos": { "planes": "mensual" | "anual" },
"corp_module_locales": { "modulo_x": [0, 1] },
"locales_mensual": [25, 30]
}
Apéndice B · Tabla de estados
fichas_alta.estado
| Estado | Significado | Quién lo asigna |
|---|---|---|
pendiente | Inicial (poco usado) | — |
completada | Cliente rellenó su parte (workflow 11) | Cliente vía solicitud.html |
rellenado | Consultor completó la ficha (workflow 04) | Consultor vía index.html |
solicitudes.estado
| Estado | Significado |
|---|---|
pendiente | Esperando que el cliente la rellene |
completado | Cliente rellenó (genera ficha) |
fichas_alta ≠ "completado" en solicitudes. Los triggers SQL ya alinean fechas con esta semántica (fecha_rellenado, fecha_completado, grabado_a3_at) para que la analítica sea coherente.
Apéndice C · Permisos de página
Cada usuario lleva permisos granulares { read, write, delete } por id de página (emitidos en el login). El rol es una base + deltas. El backend valida el permiso además de la sesión. IDs disponibles (deben coincidir con el CHECK de la tabla usuarios):
| Grupo | IDs de página |
|---|---|
| Informes | distribucion · informe_tickets · informe_tickets_ia |
| Consultor | lista · escalados · configurador · ofertas · distribuidores_detectados |
| Implementación | sinasignar · proyectos · panel_sesiones |
| Contabilidad | contabilidad · clientes_a3 · proformas |
| Customer Success | clientes · cs_kanban · bajas · promociones · highlevel · highlevel_clientes · huerfanos_prod |
| Producto | presupuestos · bolsa_horas · triaje · documento_interno_ia |
| Soporte | estado_clientes_yurest · integraciones · hardware · stock · clientes_free · resumen_semanal · resumen_mensual · documentacion_integraciones |
| Admin / Otros | admin · docs |