Doc 12 SEO Analitica y Legal - ronalc90/koptup GitHub Wiki
Resumen. El sitio está preparado para buscadores: cada página tiene su título con la plantilla «… | KopTup», una URL canónica en
https://www.koptup.com, datos estructurados (JSON-LD) y una imagen para redes;sitemap.xmlyrobots.txtdicen qué indexar y qué no. La analítica (Google Analytics 4, Google Ads y LinkedIn) está programada pero hoy no está activa en producción: depende de tres variables de Vercel y del consentimiento del visitante. En lo legal, los tres formularios que piden datos (contacto, solicitar demo y «Prueba con tu documento») exigen la autorización de la Ley 1581 de 2012 y guardan la prueba con la versión de la política que fija el servidor; el admin atiende los derechos del titular en Admin › Privacidad (buscar, exportar y suprimir) y un job anonimiza lo vencido; hay páginas de privacidad, términos y cookies que describen lo que el sistema hace de verdad, y un banner de cookies.Esta página describe lo que hace el código de
main; no es asesoría legal.
- Vista general
- SEO
- Analítica
- Legal
- Limitaciones conocidas
- Para desarrolladores: archivos clave
- Páginas relacionadas
flowchart LR
classDef visitante fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef sistema fill:#e0e7ff,stroke:#4f46e5,color:#312e81
classDef admin fill:#fef3c7,stroke:#d97706,color:#78350f
classDef externo fill:#f1f5f9,stroke:#64748b,color:#334155
subgraph SEO["SEO: para buscadores y redes"]
T["Título y descripción por página"]:::sistema
C["Canonical en www.koptup.com"]:::sistema
J["JSON-LD por página"]:::sistema
O["Imagen para redes (og:image)"]:::sistema
S["sitemap.xml y robots.txt"]:::sistema
end
subgraph AN["Analítica: solo con consentimiento"]
B["Banner de cookies"]:::visitante
G["GA4"]:::externo
A["Google Ads"]:::externo
L["LinkedIn Insight Tag"]:::externo
E["5 eventos: lead, demo, subida, WhatsApp, plan"]:::sistema
end
subgraph LE["Legal: Ley 1581"]
F["Autorización en formularios"]:::visitante
P["Prueba guardada en MongoDB"]:::sistema
PG["Páginas /privacy, /terms y /cookies"]:::sistema
end
GOO["Google y redes sociales"]:::externo
EQ["Equipo de KopTup"]:::admin
SEO --> GOO
B -->|"Aceptar"| G
B -->|"Aceptar"| A
B -->|"Aceptar"| L
E --> G
F --> P --> EQ
Tres frentes independientes: lo que ven los buscadores, lo que se mide (si el visitante acepta) y lo que se guarda de cada persona.
-
Plantilla:
"<título de la página> | KopTup", con un máximo de 60 caracteres contando el sufijo. La única excepción es el inicio, que pone la marca primero: «KopTup | IA que responde con los documentos de tu empresa». -
Dónde se definen:
apps/web/src/lib/seo-config.tstiene el título, la descripción, la canonical y la imagen de cada página; cadalayout.tsxopage.tsxlas aplica congenerateMetadata('<clave>'). Las páginas privadas (cuenta, activación) usanprivatePageMetadata(título, ruta): título propio,noindex, nofollowy canonical a su propia ruta, o ninguna si la URL lleva un token. -
Panel y portal:
/adminy/dashboardentregan en el HTML el título del área («Panel de administración | KopTup», «Mi portal | KopTup»); en el navegador cada sección lo afina, por ejemplo «Solicitudes de demo · Panel | KopTup» o «Mis demos · Mi portal | KopTup» (components/seo/AreaDocumentTitle.tsx, textos enpageTitles). -
Control automático:
node apps/web/scripts/check-titles.mjssimula cómo Next arma cada título y falla si alguno pasa de 60 caracteres o repite la marca. Corre en CI (Operación y despliegue › 3). - No se usa la meta
keywords(Google la ignora).
Títulos reales de las páginas principales (leídos del HTML que entrega el sitio):
| Ruta | <title> |
Robots |
|---|---|---|
/ |
KopTup | IA que responde con los documentos de tu empresa | index, follow |
/rag |
Sistemas RAG para empresas en Colombia | KopTup | index, follow |
/rag/salud |
RAG para salud: protocolos, normativa y auditoría | KopTup | index, follow |
/rag/legal |
Buscar en contratos con IA: RAG para áreas legales | KopTup | index, follow |
/rag/soporte |
Asistente IA para manuales internos y soporte | KopTup | index, follow |
/services |
Precios de sistemas RAG y software a medida | KopTup | index, follow |
/chatbots-ia |
Chatbots RAG para WhatsApp y web | KopTup | index, follow |
/soluciones-ia |
Soluciones de Inteligencia Artificial para Empresas | KopTup | index, follow |
/desarrollo-web-colombia |
Desarrollo Web y Software a Medida en Colombia | KopTup | index, follow |
/demo |
Prototipos Interactivos: Prueba Antes de Contratar | KopTup | index, follow |
/demo/chatbot |
Demo RAG: prueba con tu documento | KopTup | index, follow |
/solicitar-demo |
Solicitar una demo guiada | KopTup | index, follow |
/contact |
Contacto: Solicita tu Cotización Gratis | KopTup | index, follow |
/about |
Nosotros: estudio de software a medida en Bogotá | KopTup | index, follow |
/privacy, /terms, /cookies
|
Política de Privacidad / Términos y Condiciones / Política de Cookies | KopTup | index, follow |
/bienvenido-producthunt |
Product Hunt: Software a Medida con Demos | KopTup | noindex, follow |
/demo-acceso/<demo> |
«: acceso | KopTup» | noindex, nofollow |
/login, /register
|
Iniciar sesión / Crear cuenta | KopTup | noindex, nofollow |
/forgot-password, /reset-password
|
Recuperar contraseña / Nueva contraseña | KopTup | noindex, nofollow |
/auth/callback |
Iniciando sesión | KopTup | noindex, nofollow |
/activar/<token> |
Activa tu acceso | KopTup | noindex, nofollow (sin canonical) |
| Página 404 | Página no encontrada | KopTup | noindex (sin canonical) |
- El dominio canónico es
https://www.koptup.com(con www). Está fijo enapps/web/src/lib/site.ts(SITE_URL) y de ahí salen la canonical,og:url, el JSON-LD, el sitemap y los enlaces que generan las demos. -
https://koptup.com/responde con una redirección permanente (308) ahttps://www.koptup.com/. - Cada página de contenido declara su propia canonical (por ejemplo
https://www.koptup.com/rag/salud); también las páginas de cuenta. La página 404 y las rutas con un token en la URL (/activar/<token>) no declaran canonical./pricingredirige de forma permanente (308) a/services#planes-rag. -
Idiomas: español e inglés comparten la misma URL (el idioma va en la cookie
locale), así que no hayhreflang. Google ve el español, que es el idioma por defecto. Decisión: por eso eltitle, ladescriptiony las etiquetasogde cada página están en español también cuando el visitante eligió inglés (el contenido sí cambia). Traducirlos por cookie le mostraría a Google metadatos distintos para la misma URL.

A la izquierda, el inicio de /sitemap.xml; a la derecha, /robots.txt.
/sitemap.xml lo genera apps/web/src/app/sitemap.ts y hoy tiene 34 URL (16 fijas y las demos abiertas):
| Grupo | URL | Prioridad |
|---|---|---|
| Inicio | / |
1.0 |
| RAG |
/rag y /rag/salud, /rag/legal, /rag/soporte
|
0.9 y 0.8 |
| Landings y servicios |
/desarrollo-web-colombia, /chatbots-ia, /soluciones-ia, /services, /demo
|
0.9 |
| Conversión |
/solicitar-demo, /contact
|
0.8 |
| Empresa | /about |
0.7 |
| Legal |
/privacy, /terms (0.3) y /cookies (0.2) |
0.3 y 0.2 |
| Demos | Las demos Abiertas y activas en Admin › Catálogo de demos (hoy 18): /demo/chatbot, /demo/ecommerce, /demo/crm-ia… |
0.8 |
- La fecha de modificación es fija (
2026-06-29) para no marcar todo el sitio como «cambiado hoy» en cada despliegue. - Las demos «Con solicitud», «Solo por invitación» o desactivadas no se listan: a quien no tiene acceso le muestran una pantalla
noindex. - La lista de demos sale del catálogo que edita el admin (
GET /api/demo-catalog,lib/sitemap-demos.ts). El sitemap se regenera como mucho cada hora, así que un cambio de modo tarda hasta una hora en reflejarse. Si el backend no responde, usa la tabla de respaldo del código (lib/demo-access-defaults.ts).
/robots.txt (apps/web/public/robots.txt) permite todo menos las zonas privadas o con datos de sesión: /dashboard, /admin, /api/, /login, /register, /forgot-password, /reset-password, /auth/, /test, /liquidacion, /activar/, /propuesta/, /demo-acceso/ y cualquier URL con token= o sessionId=. Bingbot repite esas reglas con Crawl-delay: 5; AhrefsBot, SemrushBot, DotBot, MJ12bot, GPTBot y CCBot están bloqueados por completo. Al final declara el sitemap con www.
Tres mecanismos, del más fuerte al más débil:
| Mecanismo | Dónde | Páginas |
|---|---|---|
Cabecera X-Robots-Tag: noindex (y no-store) |
Middleware de la web | Todo /admin y /dashboard, cada demo con acceso controlado (o su pantalla de acceso) y /demo/lms/verificar
|
Cabecera X-Robots-Tag: noindex, nofollow
|
next.config.js |
/login, /register, /forgot-password, /reset-password, /auth/*, /activar/* y /propuesta/*
|
Meta robots: noindex
|
Metadatos de la página | Las páginas de cuenta, /activar/<token>, /admin, /dashboard, /demo-acceso/<demo>, /bienvenido-producthunt, /demo/cuentas-medicas, /demo/sistema-experto, /demo/lms/verificar, /embed/* y la página 404 (una sola meta robots) |
Disallow en robots.txt
|
public/robots.txt |
La lista de la sección 2.3 |
Los buscadores leen estos bloques para entender quién es KopTup, qué vende y cuáles son las preguntas frecuentes. Tipos por página (leídos del HTML del sitio):
| Página | Tipos de schema.org | Dónde se arma |
|---|---|---|
/ |
Organization, WebSite, SoftwareApplication, LocalBusiness, FAQPage
|
components/seo/StructuredData.tsx |
/rag |
BreadcrumbList, Service (con los planes como ofertas), FAQPage
|
lib/rag-plans-jsonld.ts, lib/rag-page-jsonld.ts
|
/rag/salud, /rag/legal, /rag/soporte
|
BreadcrumbList |
Página de cada sector |
/services |
BreadcrumbList, ProfessionalService, Service
|
app/services/layout.tsx |
/chatbots-ia |
BreadcrumbList, Service, FAQPage
|
lib/chatbots-page-jsonld.ts |
/soluciones-ia |
BreadcrumbList, Service, FAQPage
|
lib/ai-solutions-page-jsonld.ts |
/desarrollo-web-colombia |
BreadcrumbList, ProfessionalService, FAQPage
|
app/desarrollo-web-colombia/layout.tsx |
Cada demo abierta (/demo/<demo>) |
BreadcrumbList |
layout.tsx de cada demo |
/solicitar-demo, /contact, /about, /privacy, /terms, /cookies
|
BreadcrumbList |
layout.tsx de cada ruta |
/demo y /bienvenido-producthunt
|
Ninguno | — |
Los precios que aparecen en el JSON-LD salen del mismo archivo que los precios visibles (lib/rag-plans.ts), así que no se desalinean. El bloque Organization solo afirma lo que el sitio muestra: areaServed Colombia, perfiles en sameAs tomados de SOCIAL_LINKS (lib/site.ts, los mismos del pie de página) y preguntas frecuentes que coinciden con las páginas; no declara año de fundación, número de empleados ni países atendidos.
| Imagen | Qué es | Páginas que la usan |
|---|---|---|
/opengraph-image |
Se genera con código (app/opengraph-image.tsx), 1200 × 630, con el mensaje RAG |
Inicio, /rag y sus tres sectores, /services, /chatbots-ia, /soluciones-ia y /demo/chatbot (los destinos de los anuncios) |
/og-image.png |
Imagen fija en public/
|
Todas las demás páginas con generateMetadata
|
Twitter usa la tarjeta summary_large_image con la misma imagen. La imagen generada se ve en Mapa del sitio.
-
/llms.txt: resumen en texto de quién es KopTup y qué vende, pensado para asistentes de IA. Dice lo mismo que el sitio (sin año de fundación ni datos que no se puedan comprobar) y aclara que las demos usan datos de ejemplo. Es un archivo fijo depublic/: si cambia el dominio o la oferta, hay que editarlo a mano. -
Verificación de Google Search Console: dos códigos en los metadatos del layout raíz y un archivo
google….htmlenpublic/. -
manifest.jsone íconos para instalar el sitio en el celular.
En producción no se mide nada. Al revisar www.koptup.com el 9 de octubre de 2026 el HTML no carga ninguna etiqueta de Google ni de LinkedIn, y por eso tampoco aparece el banner de cookies: el código solo lo muestra si hay al menos una etiqueta configurada. Para encender la medición, sigue la sección 3.6.
| Etiqueta | Variable en Vercel | Formato | Categoría de cookies que exige | Qué carga |
|---|---|---|---|---|
| Google Analytics 4 | NEXT_PUBLIC_GA_ID |
G-XXXXXXXXXX |
Analítica |
gtag.js y config de GA4 |
| Google Ads | NEXT_PUBLIC_GOOGLE_ADS_ID |
AW-XXXXXXXXX o solo el número |
Marketing |
gtag.js y config de Ads (vinculador de conversiones y remarketing) |
| LinkedIn Insight Tag | NEXT_PUBLIC_LINKEDIN_PARTNER_ID |
Número | Marketing | insight.min.js |
Un ID con otro formato se ignora (la etiqueta no se carga). La CSP de next.config.js solo abre los dominios de Google o de LinkedIn si su variable existe en ese build.
sequenceDiagram
autonumber
actor V as Visitante
participant W as Web (navegador)
participant LS as localStorage
participant G as Google (gtag.js)
participant L as LinkedIn
V->>W: abre cualquier página
W->>W: ¿hay alguna etiqueta configurada en el build?
alt ninguna
W-->>V: sin banner y sin medición
else al menos una
W->>LS: lee cookie_preferences
alt nunca eligió
W-->>V: muestra el banner (salvo en /cookies)
V->>W: Aceptar, Rechazar o Configurar cookies
W->>LS: guarda la elección con la fecha
end
opt aceptó analítica o marketing
W->>W: consentimiento por defecto según su elección
W->>G: carga gtag.js y config de GA4 o Ads, sin recargar
end
opt aceptó marketing y hay Partner ID
W->>L: carga Insight Tag
end
Note over W,G: En /auth, /reset-password y /activar (la URL lleva tokens) y en /embed (el chat en el sitio de un cliente) nunca se cargan
end
Nada se carga antes de que la persona elija; «Rechazar» deja solo las cookies esenciales. En /embed/* (el chat de widget.js y del iframe en el sitio de un cliente) no se muestra el banner ni se carga ninguna etiqueta, aunque el visitante haya aceptado cookies en koptup.com: KopTup no mide a los visitantes de sus clientes (NO_TRACKING_PATHS en components/analytics/Analytics.tsx).
Detalles del consentimiento (components/analytics/Analytics.tsx y lib/cookie-consent.ts):
- La elección se guarda en
localStorage['cookie_preferences']como{ essential, functional, analytics, marketing, timestamp }. Si el navegador bloquea el almacenamiento, se respeta durante la visita. - Antes de cargar Google se declara el modo de consentimiento:
analytics_storagesegún «analítica» yad_storage,ad_user_datayad_personalizationsegún «marketing»; con marketing rechazado se activaads_data_redaction. - Si la persona retira el consentimiento en
/cookies, Google pasa adenied, GA4 se desactiva y la página se recarga para que ninguna etiqueta siga activa (LinkedIn no se puede apagar en caliente). - La elección se sincroniza entre pestañas del mismo sitio.
Cómo se ve el banner y el panel de /cookies en escritorio, con un GIF, está en Flujos del visitante › Flujo 5. En el celular:

Banner en /rag a 390 × 844 (entorno local con un ID de GA4 ficticio; las peticiones a Google se bloquearon y antes de elegir no hubo ninguna).
-
Configurar cookies: lleva a
/cookies, con un interruptor por categoría. - Rechazar: guarda «solo esenciales».
- Aceptar: activa funcionales, analítica y marketing, y carga las etiquetas en ese momento.
Todos pasan por track() (lib/analytics.ts), que no hace nada si no hay consentimiento, si la etiqueta no está cargada o si se llama en el servidor, y nunca envía datos personales (ni nombre, ni email, ni teléfono). Van a GA4 y, si está cargada, a Google Ads.
flowchart LR
classDef visitante fill:#dbeafe,stroke:#2563eb,color:#1e3a8a
classDef sistema fill:#e0e7ff,stroke:#4f46e5,color:#312e81
classDef externo fill:#f1f5f9,stroke:#64748b,color:#334155
CON["/contact"]:::visitante
SOL["/solicitar-demo"]:::visitante
CHAT["/demo/chatbot"]:::visitante
PLANES["/services, /rag y /chatbots-ia"]:::visitante
GL["generate_lead"]:::sistema
WA["whatsapp_click"]:::sistema
DS["demo_start"]:::sistema
DU["demo_upload"]:::sistema
PC["plan_click"]:::sistema
GA["GA4 y Google Ads"]:::externo
CON -->|"envío con éxito"| GL
CON -->|"botón de WhatsApp"| WA
SOL -->|"envío con éxito"| GL
SOL -->|"WhatsApp en el formulario y en la confirmación"| WA
CHAT -->|"primera pregunta"| DS
CHAT -->|"documento subido"| DU
PLANES -->|"botón de un plan o solución"| PC
GL --> GA
WA --> GA
DS --> GA
DU --> GA
PC --> GA
Cinco eventos en cuatro lugares del sitio.
| Evento | Cuándo se dispara | Parámetros | Dónde está el código |
|---|---|---|---|
generate_lead |
Formulario enviado con éxito en /contact o en /solicitar-demo
|
lead_source (contact_form o demo-request). En contacto: service (solo si es una opción del formulario) y plan_id (si viene de un plan RAG). En solicitud: demos_count y demo_slugs
|
app/contact/page.tsx, components/demo-request/DemoRequestForm.tsx
|
demo_start |
Primera pregunta en /demo/chatbot, una vez por carga de la página |
demo_mode (sample: documentos de ejemplo; upload: documento propio) |
app/demo/chatbot/page.tsx y components/demoEvents.ts
|
demo_upload |
Documento subido con éxito en «Prueba con tu documento» |
file_type (pdf, docx o txt) y pages; nunca el nombre ni el contenido |
app/demo/chatbot/components/upload/UploadDemo.tsx |
whatsapp_click |
Clic en un botón de WhatsApp |
link_location: contact_page, solicitar_demo o solicitar_demo_gracias
|
app/contact/page.tsx, components/demo-request/DemoRequestForm.tsx
|
plan_click |
Clic en el botón de un plan RAG (/services#planes-rag y /rag), en «Agenda un piloto» de /chatbots-ia o en los botones de «Otras soluciones a medida» |
plan_name (en el idioma del visitante), plan_id, plan_group (planes_rag u otras_soluciones), cta (quote, details o demo) y, en el detalle de una solución, plan_tier y modality
|
components/rag/RagPlans.tsx, app/chatbots-ia/page.tsx, components/offerings/OfferingsCatalog.tsx
|
-
Google Ads: el código no tiene etiquetas de conversión propias. Se vincula GA4 con Google Ads, se marcan en GA4 como eventos clave los que interesan (por ejemplo
generate_leadydemo_upload) y se importan en Ads como conversiones de GA4. - LinkedIn: el Insight Tag mide visitas y audiencias; las conversiones se crean en Campaign Manager (por ejemplo, por URL de la página de gracias).
- Para ver
plan_namey los demás parámetros en los informes, hay que registrarlos en GA4 como dimensiones personalizadas.
- Crea la propiedad de GA4 (y, si aplica, la cuenta de Google Ads y el Insight Tag de LinkedIn) y copia sus IDs.
- En Vercel, proyecto
koptup, entorno Production: agregaNEXT_PUBLIC_GA_ID,NEXT_PUBLIC_GOOGLE_ADS_IDy/oNEXT_PUBLIC_LINKEDIN_PARTNER_ID. -
Redespliega la web: las
NEXT_PUBLIC_*se fijan al compilar. - Abre
www.koptup.comen una ventana privada: debe aparecer el banner. Pulsa Aceptar y comprueba en el «Tiempo real» de GA4 que llega la visita. - Envía un formulario de prueba y busca
generate_leaden GA4; márcalo como evento clave y configura las conversiones (sección 3.5).
| Formulario | ¿Pide autorización? | ¿Es obligatoria? | Qué se guarda como prueba | Dónde |
|---|---|---|---|---|
Solicitar demo (/solicitar-demo) |
Sí: «Autorizo a KopTup a tratar mis datos personales para gestionar esta solicitud y contactarme sobre ella, según la Política de tratamiento de datos (Ley 1581 de 2012)» | Sí: la web no deja enviar y el backend responde 400 consent_required sin guardar nada |
aceptado, fecha, versión de la política (PRIVACY_POLICY_VERSION, hoy 2026-10), hash de la IP y navegador |
MongoDB, colección DemoRequest (campo consentimiento) |
Prueba con tu documento (/demo/chatbot) |
Sí: «Autorizo el tratamiento de mis datos personales según la Ley 1581 de 2012 y la política de privacidad» | Sí: sin ella el backend responde consent_required. Si el lead no se puede guardar, el documento no se procesa (503 lead_unavailable) |
aceptado, fecha, versión de la política, hash de la IP y navegador |
MongoDB, colección Contact con origen demo-rag (campo consentimiento) |
Contacto (/contact) |
Sí: «Autorizo a KopTup a tratar mis datos personales para responder este mensaje, según la Ley 1581 de 2012 y la política de privacidad», con campo trampa contra bots | Sí: la web no deja enviar y el backend responde 400 consent_required sin guardar nada |
aceptado, fecha, versión de la política, hash de la IP y navegador |
MongoDB, colección Contact con origen contact-form (campo consentimiento); se ve en Admin › Contactos |
Registro (/register) y activación (/activar/<token>) |
No (la activación viene de una solicitud que ya la tiene) | — | — | MongoDB, colección User
|

El formulario con todos los datos llenos menos la autorización, después de pulsar «Enviar solicitud».
- Casilla de autorización, sin marcar por defecto.
-
Enlace a la política (
/privacy). - Error si no se marca: «Debes autorizar el tratamiento de tus datos para enviar la solicitud.»

La única información personal que pide la demo con documento propio es el email.
- Casilla de autorización (obligatoria).
- Enlace a la política de privacidad.
- Email: «Es lo único que te pedimos».

Final del formulario de /contact después de pulsar «Enviar mensaje» sin marcar la casilla.
- Casilla de autorización, sin marcar por defecto.
-
Enlace a la política de privacidad (
/privacy). - Error junto a la casilla: «Marca la casilla de autorización para enviar el mensaje.» Arriba del formulario aparece «Revisa los campos marcados.»
Los errores que devuelve el servidor también se muestran junto al campo que falló (por ejemplo, «El nombre es muy largo (máximo 120 caracteres).»); el límite de envíos y la falta de conexión tienen su propio mensaje.
De punta a punta, la autorización de una solicitud de demo queda a la vista del equipo:

GIF: la persona autoriza y envía; el equipo ve la solicitud y, en el detalle, la fecha y la versión de la política.

Admin › Solicitudes de demo › detalle (persona y empresa ficticias).
- Autorización de datos: «Autorizó el tratamiento de sus datos (Ley 1581 de 2012) el · política 2026-10».
- Página de origen desde la que se envió.
Si la misma persona vuelve a enviar el formulario con una solicitud abierta de menos de 30 días, las demos se suman a esa solicitud y la prueba de la autorización se reemplaza por la nueva.
Cuando publiques una política nueva: actualiza el texto de /privacy (en apps/web/messages/es.json y en.json, claves privacyPage, incluida la versión que muestra la página), cambia PRIVACY_POLICY_VERSION en Railway (por ejemplo 2027-01) y redespliega los dos. Las autorizaciones nuevas quedarán con la versión nueva y las viejas conservan la suya. El backend siempre guarda su propia versión: la que envíe un formulario se ignora.
| Origen | Datos personales | Dónde quedan | Quién más los recibe |
|---|---|---|---|
| Solicitar demo | Nombre, empresa, cargo, email, teléfono, país, tamaño de empresa, demos, caso de uso, página de origen, referente y UTM | MongoDB: DemoRequest y una copia como lead en Contact (origen demo-request) |
Aviso al dueño por correo (Resend) y WhatsApp (Meta) si están configurados; acuse al solicitante si hay correo configurado |
| Contacto | Nombre, email, teléfono, empresa, servicio, presupuesto, plazo para empezar, la cotización que venía revisando (plan, nivel y modalidad) y mensaje | MongoDB: Contact (con el estado del aviso por canal en notificaciones) |
Aviso al dueño por correo, enviado por Resend, con todos los datos y el mensaje; y por WhatsApp, enviado por Meta (WhatsApp Business Platform), con nombre, empresa, servicio, email y un extracto del mensaje (sin el teléfono) |
| Prueba con tu documento | Email y el documento | Email en Contact (origen demo-rag); el documento, solo en memoria y por máximo 1 hora |
OpenAI recibe los fragmentos relevantes y la pregunta, no el documento completo ni el email |
| Chatbot | Bots de «Configura el tuyo» con sus documentos, y las preguntas al widget de un bot de usuario | MongoDB (chatbot_bots, chatbot_bot_chunks, chatbot_turns); las preguntas vencen a los 30 días y las de «Prueba el asistente» y la vista previa no se guardan |
OpenAI recibe la pregunta, los fragmentos y el historial reciente |
| Cuenta del prospecto | Email, nombre, empresa, teléfono y contraseña cifrada (hash) | MongoDB: User, sus accesos en DemoGrant y el enlace de activación solo como hash en MagicLinkToken
|
— |
| Acciones del equipo | Quién hizo qué y hash de la IP | MongoDB: AuditLog
|
— |
| Navegación | IP, ruta y navegador de cada petición | Registros del backend en Railway | — |
La IP nunca se guarda en claro en MongoDB: solo su hash. Cuánto tiempo se conserva cada dato y cómo se borra está en Flujos técnicos › 8.3; los campos de cada colección, en Modelos de datos.
Las tres comparten diseño (encabezado azul con la fecha, secciones numeradas y una nota final), tratan de «tú» como el resto del sitio, están en español y en inglés, su pie muestra el año en curso y sus textos viven en apps/web/messages/es.json y en.json (privacyPage, termsPage, cookiesPage). Describen solo lo que el sistema hace de verdad; no las ha revisado un abogado (es una acción pendiente del dueño, ver Estado y próximos pasos). Las capturas de cada una completa están en Páginas públicas › 13.
| Página | Qué dice hoy |
|---|---|
/privacy |
Versión 2026-10. Responsable (KopTup, dirección, email y teléfono); qué datos recoge cada formulario y para qué (contacto, solicitar demo, «Prueba con tu documento», cuenta y portal, chatbot, navegación); encargados reales (Vercel, Railway, MongoDB, Redis, OpenAI, Resend para los correos de la plataforma (avisos al equipo, solicitud de demo y cuenta), Meta con WhatsApp Business Platform para el aviso al equipo de un nuevo contacto (con nombre, empresa, servicio, email y un extracto del mensaje) y de los pedidos y solicitudes de propuesta del portal (nombre, email, monto e ítems o el mensaje) y Amazon S3 si se activa para los archivos del gestor de documentos; Google Analytics, Google Ads y LinkedIn solo con consentimiento; Google al entrar con Google); plazos de conservación (24 meses sin actividad, 30 días del chatbot, 1 hora del documento, 7 días de sesión); seguridad; base legal y datos sensibles; derechos de la Ley 1581 (incluida la queja ante la SIC); cómo ejercerlos y plazos (consultas en 10 días hábiles, reclamos en 15); cookies; transferencias internacionales; menores; cambios; contacto |
/terms |
Condiciones del servicio de desarrollo a medida: aceptación, servicios (incluidas las demos y las funciones con IA: datos de ejemplo, la IA puede equivocarse, no subir información confidencial), precios y pagos, propiedad intelectual, confidencialidad, plazos, garantías, soporte, terminación y ley aplicable |
/cookies |
Qué son; tabla con las cookies y claves del navegador reales por categoría (esenciales, analítica y publicidad), con interruptores para las dos opcionales; botones «Aceptar todas», «Solo esenciales» y «Guardar preferencias» con confirmación en la página; cómo controlarlas en el navegador y cookies de terceros (Google y LinkedIn) |

La sección «Tus derechos» de la política de privacidad.
- Los derechos de la Ley 1581, entre ellos quejarse ante la Superintendencia de Industria y Comercio.
- Cómo ejercerlos: escribiendo al email de contacto, con lo que se pide y cómo verificar que eres el titular.
- Plazos de respuesta de la ley: 10 días hábiles para consultas y 15 para reclamos.
Cuando llega una solicitud, el admin la atiende en Admin › Privacidad: busca a la persona por su email, le entrega sus datos en un JSON o los suprime (ver Manual del administrador › Privacidad).
El banner y su panel de preferencias están en la sección 3.3. La tabla de /cookies lista lo que el sitio guarda de verdad en el navegador (revisado en el código), en tres categorías: esenciales (siempre activas), analítica y publicidad (opcionales). No hay cookies «funcionales» opcionales: la categoría se quitó de la página y se guarda apagada.

Las esenciales que lista la página.
-
accessTokenyrefreshToken: la sesión iniciada. -
locale: el idioma; solo se crea si la persona cambia el idioma (por eso es esencial: guarda una elección que ella pidió). -
cookie_preferences: la elección de cookies, en el almacenamiento local. - Claves de las demos (
koptup…,demo:…): datos de ejemplo y llaves de dueño de los bots del chatbot.
| Categoría | Nombres | Cuándo existen |
|---|---|---|
| Esenciales |
accessToken (15 min), refreshToken (7 días), locale (1 año), y en el almacenamiento del navegador cookie_preferences, theme, user, redirectAfterLogin (hasta cerrar la pestaña), preferencias del panel y del portal, y las claves de las demos |
Siempre que se usan esas funciones |
| Analítica |
_ga, _ga_<ID> (2 años) |
Solo si la persona la acepta y si NEXT_PUBLIC_GA_ID está configurada (hoy no) |
| Publicidad |
_gcl_au, _gcl_aw (90 días); li_fat_id (30 días) y, en el dominio de LinkedIn, bcookie (1 año), lidc (1 día) y UserMatchHistory (30 días) |
Solo si la persona la acepta y si las etiquetas de Google Ads o LinkedIn están configuradas (hoy no) |
Al guardar, la página confirma en el propio panel («Guardamos tus preferencias.»), sin ventanas emergentes del navegador.
| # | Qué pasa | Efecto |
|---|---|---|
| 1 | La analítica no está configurada en producción | No hay datos de tráfico ni de conversiones, y no aparece el banner |
| 2 | Los textos legales no los ha revisado un abogado. Describen lo que el sistema hace, con los plazos de la Ley 1581, pero el responsable debe validarlos (razón social y NIT del responsable, plazo de conservación de 24 meses, transferencias internacionales) | Acción del dueño (Estado y próximos pasos) |
| 3 | Los contactos anteriores a la casilla de autorización no tienen prueba guardada | Se anonimizan con el plazo de conservación si no tienen actividad, o el admin los suprime a pedido |
| 7 | El sitemap tarda hasta una hora en reflejar un cambio de modo en Admin › Catálogo de demos | Una demo recién cerrada puede seguir listada un rato (y ya muestra una pantalla noindex) |
| Tema | Archivo |
|---|---|
| Dominio, marca, plantilla de títulos, imagen RAG, perfiles sociales | apps/web/src/lib/site.ts |
| Títulos, descripciones, canonical e imagen por página | apps/web/src/lib/seo-config.ts |
| Metadatos por defecto, verificación de Google | apps/web/src/app/layout.tsx |
| Control de títulos | apps/web/scripts/check-titles.mjs |
| Sitemap |
apps/web/src/app/sitemap.ts, apps/web/src/lib/sitemap-demos.ts
|
| Metadatos de páginas privadas y títulos del panel |
privatePageMetadata en lib/seo-config.ts, components/seo/AreaDocumentTitle.tsx
|
Robots, llms.txt, imagen fija |
apps/web/public/robots.txt, public/llms.txt, public/og-image.png
|
| Imagen generada | apps/web/src/app/opengraph-image.tsx |
| JSON-LD |
components/seo/StructuredData.tsx, lib/rag-plans-jsonld.ts, lib/rag-page-jsonld.ts, lib/chatbots-page-jsonld.ts, lib/ai-solutions-page-jsonld.ts y los layout.tsx de cada ruta |
| Etiquetas y eventos |
apps/web/src/lib/analytics.ts, components/analytics/Analytics.tsx
|
| Consentimiento |
apps/web/src/lib/cookie-consent.ts, components/consent/CookieBanner.tsx, app/cookies/page.tsx
|
| CSP de las etiquetas | apps/web/next.config.js |
| Autorización en «Solicitar demo» |
components/demo-request/DemoRequestForm.tsx, apps/backend/src/routes/demo-public.routes.ts, services/demo-requests.service.ts, models/DemoRequest.ts
|
| Autorización en «Prueba con tu documento» |
app/demo/chatbot/components/upload/UploadForm.tsx, apps/backend/src/routes/demo-rag.routes.ts
|
| Versión de la política |
PRIVACY_POLICY_VERSION (apps/backend/src/config/demos.ts) |
| Autorización en el formulario de contacto |
apps/web/src/app/contact/page.tsx, apps/backend/src/routes/contact.routes.ts, controllers/contact.controller.ts, models/Contact.ts (consentimiento) |
| Derechos del titular (buscar, exportar y suprimir) |
apps/web/src/app/admin/privacidad/page.tsx, apps/backend/src/routes/admin-privacy.routes.ts, services/privacy.service.ts
|
| Plazo de conservación |
apps/backend/src/services/retention.service.ts, jobs/retention.job.ts (DATA_RETENTION_MONTHS) |
| Conversaciones del chatbot (vencimiento) |
apps/backend/src/services/chatbot-store.service.ts, models/ChatbotBot.ts, apps/web/src/app/embed/chatbot/[botId]/embedChannel.ts
|
| Textos legales |
apps/web/messages/es.json y en.json (privacyPage, termsPage, cookiesPage, cookieBanner) |
-
Páginas públicas: capturas completas de
/privacy,/termsy/cookies. - Flujos del visitante: banner y preferencias de cookies paso a paso, y qué mide cada flujo.
- Flujos técnicos › 8: recorrido de los datos personales y retención.
- Operación y despliegue: variables de entorno y cómo redesplegar.
- Modelos de datos · Glosario y preguntas
- Plan original: Comercial, marketing y legal, Legal y Landings SEO.