Yurest API · v1

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).

Convención de respuesta: casi todos los endpoints devuelven 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

Basic Auth RETIRADO (auditoría jun-2026). La credencial compartida 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

ParteContenido
userIdID del usuario autenticado (tabla usuarios).
expEpoch de expiración. TTL 30 días si «recordar», 8 h si no.
hmacHMAC_SHA256(userId|exp, SECRET). El secreto vive solo en n8n.
Modo Laravel (migración en curso): si 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.

EndpointAutorizaciónPor qué es público
auth/loginusuario + contraseñaEmite el token de sesión.
responderSolicitudaccess_tokenEl cliente rellena la ficha desde el email.
completarFicha?t=<token>token de solicitudPrecarga datos del formulario del cliente.
oferta-publica?t=<token>token de ofertaVista pública de la oferta (configurador).
pago/iniciartoken de ofertaEl cliente paga desde la oferta pública (Redsys).
oferta-cambios (POST)token de ofertaTelemetría de cambios del cliente en la oferta (fire-and-forget).

Errores y diagnóstico

StatusSignificadoCuándo
200OK · revisa body.successCasi todas las respuestas (incluso fallos lógicos vienen 200).
401 / 403Sesión expiradaFrontend redirige a login.html.
500Error interno del workflowExcepción no controlada en n8n.
Patrón de diagnóstico: los workflows de escritura críticos (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

POST /webhook/auth/login Público

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": [...] }
  }
}
El front valida estrictamente success && user.username && user.rol. Credenciales inválidas → mensaje neutro (no distingue usuario inexistente de contraseña incorrecta).
GET /webhook/auth/verify?userId=<id> X-Session-Token

Verifica que la sesión sigue viva (chequeo periódico del portal). Un 401/403 fuerza logout.

GET POST /webhook/auth/usuarios · /webhook/auth/roles X-Session-Token

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 }.

EndpointUso
auth/usuariosCRUD de usuarios + permisos (create/update/password/delete).
auth/rolesCatálogo de roles base y sus permisos por defecto.
auth/usuarios/historialGET del audit log de acciones sobre usuarios (pestaña «Historial»). Tabla usuarios_historial.

📋 Fichas

GET /webhook/018f3362-7969-4c49-9088-c78e4446c77f X-Session-Token

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

POST /webhook/57e04029-bae4-4124-8c43-c535e831a147 X-Session-Token

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)

CampoTipoNotas
iduuidSi presente → UPDATE; ausente → INSERT
Nombre SociedadstringMapea a denominacion
CIF/NIFstringMapea a cif
Email · Email Factura · Email CCstring
Tipo Clientestringlite · planes · corporate
Estadostring"rellenado" cuando consultor completa
JP * · Firmante * · TPV *stringsBloques de datos
Lite · Distribuidor"Sí" / ""Booleanos como string
AdjuntosarrayPDF/JPG/PNG en base64
SEPAobjectMandato firmado (datos + firma_base64)
paquetes_carritoobjectSnapshot del carrito (módulos, precios, periodos)
localesarrayLocales 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.

GET /webhook/5a304fcd-ae1d-49e6-92d1-c5a5e007bbfd?t=<token>|?id=<id> Público

Workflow 12-completar-ficha. Devuelve los datos de una ficha o solicitud para precargar el formulario. Acepta tres formas de identificación.

Query params

ParamTipoUso
tstring (16-128 chars)Token aleatorio público (preferente)
iduuid o númeroUUID 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" }
POST /webhook/a2b1b1d6-a1dc-4366-b60e-b5e4506faa3d X-Session-Token

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": []
}
success: false: revisa errores (problemas Supabase) o si ficha_count + sol_count == 0 → el id ya no existía.
POST varios · ver tabla X-Session-Token

Acciones puntuales sobre una ficha ya creada (no re-guardan la ficha entera). Todas reciben { ficha_id } y devuelven { ok, ... }.

PathWorkflowEfecto
/ficha/notificar-completaWF19Al completar la ficha: email Drive al cliente + tarea Asana + email integraciones. Antes vivía en WF11.
/ficha/introducir-yurestWF64Flag «Introducida en Yurest» (idempotente). 1ª vez envía aviso. → { ok, introducido_yurest, introducido_yurest_at }.
/ficha/marcar-ganado-hlWF65{ ficha_id, marcar_ganado:true } → mueve la oportunidad HighLevel a won + «Ganados Planes» y escribe MRR/ARR/nº locales/periodicidad.
/contrato-enviarWF84Envía el contrato a firmar (efirma) → { ok, id_operacion, destinatarios }. Con { preview:true } devuelve el HTML sin enviar.
/fichas-adjuntos-uploadWF35{ nombre, tipo, data(dataURL), ficha_id? } → sube al bucket Storage. → { storage_path, signed_url, size }.
/sepa/solicitar-firmaWF70{ ficha_id, mandato_id, email } → envía al cliente enlace a firmar-sepa.html (token HMAC).
/fichas/importar-sheetWF54{ sheetUrl } → importa ficha desde Google Sheets. → { ok, ficha, avisos }.
GET POST /webhook/cliente-notas · /cs-estado · /ficha-asignar-promo X-Session-Token

Modal «Ver cliente» (clientes.html): notas internas, movimiento en el Kanban CS y asignación de promoción.

PathMétodoUso
/cliente-notasGET ?ficha_id= / POSTNotas internas firmadas con el email del JWT. GET → { notas:[{id,autor_email,texto,created_at}] }; POST { ficha_id, texto }.
/cliente-notas-editar · /cliente-notas-borrarPOSTEditar / borrar una nota.
/cs-estadoPOSTMueve el cliente entre columnas del Kanban CS. Actualiza cs_estado + cs_estado_historial.
/ficha-asignar-promoPOST{ ficha_id, promo_id, promo_turno } → patch de promocion_id/promocion_turno/promo_asignada sin re-guardar la ficha.

📨 Solicitudes

POST /webhook/b0629324-e611-47d4-835f-3ac9bcd4dc9b X-Session-Token

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 }
GET POST listaSolicitudes · listaRellenado · prellenarSolicitud · eliminarSolicitud X-Session-Token

Listados y utilidades del flujo de solicitudes.

PathMétodoUso
/webhook/1757fdcc-… (listaSolicitudes)GETWF08. Solicitudes NO completadas → { solicitudes:[{id,access_token,"ID Solicitud","Nombre Sociedad",Email,Estado,Fecha}] }.
/webhook/fa16b994-… (listaRellenado)GETWF09. Solicitudes con estado=completada (esperan al consultor) → { items:[{id,"Nombre Sociedad",Email,Consultor}] }.
/solicitud-prellenar-clientePOSTWF38. 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)POSTMismo webhook que eliminarFicha (soft-delete). { action:'delete', entity:'solicitud', id }.
POST /webhook/6da4274f-5a6d-4981-a92a-f9d7eb734144 Público

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_alta con estado=completada + SEPA persistido.
  • Crea carpeta en Google Drive y guarda carpeta_drive en la ficha.
  • Email al cliente con enlace al Drive.
  • Email al consultor avisando.
  • UPDATE solicitudes: estado=completado + ficha_id (dispara trigger que copia fecha_solicitud).
  • Si TPV no integrado: tarea en Asana (proyecto Integraciones 1207920061546505) + email a soporte/a.jareno/luis.

Respuesta 200

{ "success": true }

💼 Ofertas y pagos

GET POST /webhook/ofertas X-Session-Token

Workflow 30-ofertas. Ofertas generadas por el configurador (departamento Consultor). GET lista con filtros; ?id=<uuid> devuelve una sola.

POST — action

actionEfecto
create · updateAlta/edición de la oferta.
archivar · reactivarEstado de archivo.
cambiar_estadoenviada (email) / ganada (ficha arrancada) / etc.
vincular_fichaAsocia la oferta a una ficha de alta.
proforma_serie lleva DOS formatos y NINGÚN consumidor debe asumir correlatividad: ofertas → PRO-YYYY-<uuid8> (preview, sin numeración fiscal); pedidos hardware (WF21) → PRO-AAAA-NNNN correlativa reservada por RPC.
GET POST oferta-publica · oferta-enviar-comercial · oferta-cambios Mixto
PathAuthUso
/oferta-publica?t=<token>Público (token)WF36. Vista pública de la oferta (configurador.html?t=).
/oferta-enviar-comercialX-Session-TokenWF37. { 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-cambiosPOST público · GET HMACWF80. 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.
POST /webhook/pago/iniciar · /webhook/pago/enviar-ficha-link Público

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»).

GET POST /webhook/escalados X-Session-Token

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

POST /webhook/yurest-grabado-a3 X-Session-Token

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": []
}
GET /webhook/contabilidad/clientes-a3 X-Session-Token

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

GET POST PUT DELETE /webhook/proyectos X-Session-Token

Workflow 01-proyectos-crud. CRUD de proyectos de implementación.

MétodoBodyRespuesta
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.

PUT /webhook/proyectos/tarea X-Session-Token

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 }
PUT /webhook/proyectos/tarea/mover · /webhook/proyectos/tarea/eliminar X-Session-Token

Mover una tarea entre secciones (drag & drop kanban) o eliminarla.

Body — mover

{ "proyectoId": "uuid", "tareaId": "...", "seccionOrigen": "...", "seccionDestino": "..." }
GET POST /webhook/proyectos/mensajes · /webhook/proyectos/historial X-Session-Token

Hilo cliente ↔ staff e historial del proyecto (WF01).

PathMétodoUso
/proyectos/mensajesGET ?proyectoId={ ok, mensajes:[…] } (asc). El mismo hilo que ve el cliente en espacio-cliente.html.
/proyectos/mensajesPOST{ proyectoId, texto } (máx 4000). El backend fija autor_tipo='staff' y el nombre a partir del token.
/proyectos/historialGETTimeline de acciones del proyecto.
GET POST proyectos/asana-import · asana/project-sections · asana/tasks · asana/task/stories X-Session-Token

Proxy e importadores de Asana (las credenciales viven solo en n8n).

PathUso
/asana/tasks?projectId= · /asana/task/stories?taskId=WF07. Proxy de lectura: tareas de un proyecto e historias (comentarios) de una tarea.
/proyectos/asana-importWF81. Importa proyectos arrancados en Asana. POST { op:'tasks'|'detalles' }.
/asana/project-sectionsLista secciones de un proyecto (pestaña Formación grupal: el CS escoge qué sección representa al cliente).

🎓 Promociones

GET POST promociones · promociones/asana[/ocupar] · promociones/asana-import-tareas X-Session-Token

Tandas de implementación (Customer Success): 16 plazas (8 mañana + 8 tarde). Doble proyecto Asana por promoción.

PathUso
/promocionesVista 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/ocuparWF63. { project_gid, title } → renombra la 1ª sección libre → { ok, section_gid?, nuevo_titulo?, libres_restantes? }.
/promociones/asana-import-tareasWF83. { promocion_id, turno } → importa tareas por sección/turno → { ok, promo, actualizados, huerfanas, sinSeccion }.

📅 Sesiones y calendario

GET POST /webhook/calendar/event · /webhook/calendar/disponibilidad X-Session-Token

Workflow 07-calendar-asana. Google Calendar del implementador.

PathUso
/calendar/event (POST)Crea/actualiza un evento (sesión de implementación). { summary, start, end, attendees[] }. El reagendado actualiza por googleEventId.
/calendar/disponibilidadLee el calendario en un rango para detectar solapes al agendar.
POST /webhook/panel/sesion-resumen · /webhook/panel/enviar X-Session-Token

Panel de sesiones de implementación (WF52).

PathUso
/panel/sesion-resumenDado un evento ya finalizado, lee las notas de Gemini → { show, resumen, duracionMin }.
/panel/enviarEnvía el email de resumen (Gmail real): { implementador, correosInvitados, titulo, html }.

🖥️ Hardware

GET POST /webhook/hardware/pedidos · /webhook/hardware/stock X-Session-Token
PathUso
/hardware/pedidosPedidos por proyecto: ciclo solicitada → proforma → pago → lista_envío. Consumido desde proyecto.Hardware, contabilidad.Proformas y Soporte envíos.
/hardware/stockCatálogo del almacén (Soporte): stock actual, mínimo, precios y movimientos. Acciones: create, update, archivar, reactivar, movimiento.

🧩 Producto

GET POST presupuestos · presupuestos-asana[-attach] · presupuestos/enviar-cliente X-Session-Token

Desarrollos a medida por cliente (reemplaza el Excel de Producto).

PathUso
/presupuestosCRUD: quién paga, estado de aprobación y de entrega.
/presupuestos-asanaImporta tareas de la sección «Pendiente de presupuesto» (Back Clientes). POST { action:'import'|'refresh', asana_gid }.
/presupuestos-asana-attachAdjunta el PDF generado a la tarea Asana de origen (base64 → multipart).
/presupuestos/enviar-clienteWF22. Envía el PDF por email al cliente (Gmail soporte@).
GET POST /webhook/bolsa-horas · /bolsa-horas-* · /bolsa-horas/asignacion* X-Session-Token

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.

PathUso
GET /bolsa-horas{ compras:[…], asignaciones:[…] }.
/bolsa-horas-crearSube ambos PDFs e inserta la compra.
/bolsa-horas-editar · -archivar · -reemplazar-pdfEditar horas/fecha, archivar, reemplazar un PDF.
/bolsa-horas/asignacion · -borrarUpsert / borra una asignación bolsa↔presupuesto.
GET POST /webhook/triaje/asana[/edit|/orden] X-Session-Token

Workflow WF57. Lee en vivo el proyecto Asana «Sugerencias optimizaciones clientes» (tareas + secciones + etiquetas + campos, normalizado y paginado).

PathUso
GET /triaje/asanaSugerencias normalizadas.
/triaje/asana/editEdita una sugerencia → escribe en Asana (PUT name/notes + add/removeTag de módulo).
/triaje/asana/ordenOrden de prioridad manual compartido (mapa módulo→[gids], Data Table triaje_orden).
GET POST /webhook/documento-ia[/analizar|/generar|/borrar|/preguntar|/editar] X-Session-Token

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.

PathUso
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[] }.
GET POST /webhook/newsletter[/analizar|/vincular|/cargar|/guardar|/generar|/enviar-prueba|/borrar|/asignar-tipo] X-Session-Token

Workflow WF73. Newsletter de sprint (/updates). Analiza un proyecto Asana, reescribe títulos con IA y arma el email.

PathUso
/newsletter/analizar · /vincularDetecta proyecto/sección/campos; vincula la newsletter.
GET /newsletter · /cargar · /guardarLista / carga (merge en front) / guarda { titulo, intro, items }.
/newsletter/generar{ id, task_gids[] }{ items:[{task_gid, titulo_reescrito, beneficio}] }.
/newsletter/enviar-prueba · /borrar · /asignar-tipoEnvío de prueba, soft delete, escribe el campo «Tipo» en Asana.

🤝 Customer Success

GET POST /webhook/highlevel/* · /webhook/highlevel/prod-sin-crm X-Session-Token

Integración HighLevel (CRM). Credenciales (LOCATION_ID + TOKEN) solo en n8n.

PathUso
/highlevel/contactosWF40. Listado de contactos. Filtros server-side: ?limit(1-500), ?cursor("ts,id"), ?q, ?tag, ?origen.
/highlevel/contacto-notas · /tagsNotas del CRM de un contacto bajo demanda; catálogo de tags del location.
/highlevel/clientes-ganadosWF49. Snapshot de oportunidades ganadas (3 pipelines) enriquecidas con contacto/business. Formato { ok, total, columnas, rows, generated_at }.
/highlevel/clientes/refreshWF50. Refresca el snapshot on-demand (cron 6:30 UTC).
/highlevel/prod-sin-crmWF51. Clientes vivos en producto SIN oportunidad ganada (match prod_id↔id_bbdd, fallback CIF).
/highlevel/oportunidades-abiertas · /contacto-detalleWF67. Picker de oferta desde oportunidad abierta.
GET POST /webhook/clientes-free[/toggle|/refresh|/asana-task] X-Session-Token

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.

GET /webhook/home/kpis-hoy X-Session-Token

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

GET zendesk/tickets-heatmap[-ia] · zendesk/resumen-semanal · zendesk/resumen-mensual X-Session-Token
PathUso
/zendesk/tickets-heatmapMapa de calor: ?from=YYYY-MM-DD&to=YYYY-MM-DD → matriz [día 0-6][hora 0-23].
/zendesk/tickets-heatmap-iaIgual pero solo tickets aún en el agente IA (asunto «Conversation with»): cobertura del bot.
/zendesk/resumen-semanalResumen ejecutivo IA de la semana. ?week=actual|anterior o ?from&to.
/zendesk/resumen-mensualWF29. Análisis profundo (gpt-4o) con cache por (año, mes). ?refresh=1.
GET POST zendesk/orgs · cliente/zendesk-link · zendesk/tickets-cliente · registro-error* X-Session-Token

Asociación cliente ↔ organización Zendesk (modal «Ver cliente» → Tickets) y registro de errores.

PathUso
/zendesk/orgsWF41. Lista organizaciones con filtro server-side (nombre/external_id/details/notes).
/cliente/zendesk-linkWF42. PATCH zendesk_org_id (o NULL). 409 si la org ya está asociada a otro cliente.
/zendesk/tickets-clienteWF43. Tickets de la org asociada (ventana default 180d).
/registro-error/org-users · /registro-errorWF67. Crea ticket Zendesk + tarea Asana enlazados → { ok, ticket_id, ticket_url, asana_gid, asana_url }.
GET POST incidencias-dev/{estado,rebuild} · integraciones-tpv/{estado,rebuild} X-Session-Token

Snapshots diarios (06:00) + refresco manual. Cruce Zendesk × Asana.

PathUso
GET /incidencias-dev/estadoÚltimo snapshot de incidencias DEV.
POST /incidencias-dev/rebuildReconstruye el snapshot.
/integraciones-tpv/estado · /rebuildGemelo para integraciones TPV (filtro por proveedor).
GET POST /webhook/cliente/asana-formacion-link · /webhook/asana/formacion-tasks X-Session-Token

Pestaña Formación · proyecto Asana asociado al cliente.

PathUso
/cliente/asana-formacion-linkWF44. { 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:[…] }.
GET POST /webhook/9e0fc21e-… · /2c62e049-… · /6c6b655b-… · /buscar-resumen · /042f57e0-… X-Session-Token

Mini-app «Churn técnico» (iframe). Webhooks autenticados por el Auth Gate HMAC: churnClientes, churnResumenGenerar, churnNiveles, churnResumenBuscar, churnTickets.

GET POST /webhook/notif-integraciones/{config,grupos,historial} X-Session-Token

Notificaciones automáticas de Integraciones: configuración, grupos de destinatarios e historial de envíos.

📦 Producto Yurest (snapshot prod)

GET POST /webhook/prod/clientes[/refresh|/nombres] X-Session-Token

Uso del producto Yurest cacheado en Supabase (prod_clientes_snapshot) para evitar la query pesada al MySQL de producción en cada apertura.

PathUso
GET /prod/clientesWF47. Lee el snapshot → { ok, total, refreshed_at, clientes:[…] }.
POST /prod/clientes/refreshWF48. Reejecuta la query MySQL (cron 6am UTC). Tarda 5-15s → { ok, count, refreshed_at }.
GET /prod/clientes/nombresLista ligera id+nombre+nombre_comercial de TODA la tabla (para casar nombre HL → id cliente).

⚙️ Administración

GET POST /webhook/admin/passwords · /webhook/admin/passwords/reveal X-Session-Token

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.

PathUso
GET /admin/passwordsLista 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.
GET POST /webhook/admin/equipo X-Session-Token

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étodoUso
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.
GET POST /webhook/historial · /webhook/historial/global X-Session-Token

Audit log.

PathUso
/historialHistorial de acciones por ficha.
/historial/globalFeed 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

GET POST /webhook/84f094b2... · /webhook/73ce8d34... · /webhook/95d5ed5d... X-Session-Token

Workflow 05-bajas. Gestión de bajas de clientes (registro, edición de campos individuales y borrado).

EndpointOperación
bajasGET listado
bajaCliente · bajaLocal · bajaModulos · bajaEditarPOST campos específicos (mismo webhook con action)
bajaBorrarPOST borra una baja

📊 Distribución

POST /webhook/6d3ed726-c86a-4b86-a2ae-7f07da9630a5 X-Session-Token

Workflow 06-distribucion. Asigna un implementador a una ficha.

Body

{ "fichaId": "uuid", "implementador": "Carlos Aparicio" }
GET POST /webhook/distribuidor/detectados · /webhook/distribuidor/detectados-estado X-Session-Token

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.

PathUso
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

GET /webhook/2010bb2b-... · /webhook/bdef8517-... X-Session-Token

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

EstadoSignificadoQuién lo asigna
pendienteInicial (poco usado)
completadaCliente rellenó su parte (workflow 11)Cliente vía solicitud.html
rellenadoConsultor completó la ficha (workflow 04)Consultor vía index.html

solicitudes.estado

EstadoSignificado
pendienteEsperando que el cliente la rellene
completadoCliente rellenó (genera ficha)
Nota semántica: los nombres son legacy. "completada" en 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):

GrupoIDs de página
Informesdistribucion · informe_tickets · informe_tickets_ia
Consultorlista · escalados · configurador · ofertas · distribuidores_detectados
Implementaciónsinasignar · proyectos · panel_sesiones
Contabilidadcontabilidad · clientes_a3 · proformas
Customer Successclientes · cs_kanban · bajas · promociones · highlevel · highlevel_clientes · huerfanos_prod
Productopresupuestos · bolsa_horas · triaje · documento_interno_ia
Soporteestado_clientes_yurest · integraciones · hardware · stock · clientes_free · resumen_semanal · resumen_mensual · documentacion_integraciones
Admin / Otrosadmin · docs