Docs

Sistema de Invitaciones

Documento tecnico que describe el sistema completo de invitaciones en Nvito: maquina de estados, flujos de creacion (internas y externas), templates, secciones, versionamiento, generacion HTML y analiticas.

CampoValor
Version1.1
FechaMarzo 2026
EstadoEn revision
AudienciaEquipo de desarrollo, arquitectos, stakeholders

1. Maquina de Estados

Cada invitacion en Nvito sigue un ciclo de vida estricto controlado por una maquina de estados. El servicio ApprovalWorkflowService define las transiciones validas y garantiza que ningun cambio de estado ocurra fuera de las reglas establecidas.

1.1 Diagrama de Estados

1.2 Transiciones Detalladas

TransicionDeAQuien la disparaValidacionesEndpoint
SubmitIN_CONSTRUCTIONUNPUBLISHEDUsuario (owner/admin)Schema no vacio, secciones requeridas presentes (solo internas)POST /invitations/:id/submit-for-approval
ApproveUNPUBLISHEDPUBLISHEDUsuario (owner/admin)Internas: genera HTML y sube a CDN. Externas: verifica que htmlUrl exista. Activa evento si esta en DRAFTPOST /invitations/:id/approve
RejectUNPUBLISHEDIN_CONSTRUCTIONUsuario (owner/admin)Requiere razon de rechazoPOST /invitations/:id/reject
Request ChangesUNPUBLISHEDIN_CONSTRUCTIONUsuario (owner/admin)Requiere descripcion de cambiosPOST /invitations/:id/reject (action: request_changes)
UnpublishPUBLISHEDUNPUBLISHEDUsuario (owner/admin)Webhook de despublicacion enviadoPOST /invitations/:id/unpublish
ClosePUBLISHEDCLOSEDUsuario o sistema (auto-complete/cancel)Registra closedBy y closeReasonPOST /invitations/:id/close
ReopenCLOSEDPUBLISHEDUsuario (owner/admin)Evento debe ser ACTIVE, fecha no debe haber pasadoPOST /invitations/:id/reopen

1.3 Sincronizacion con Eventos

La maquina de estados de invitaciones esta sincronizada con el ciclo de vida de eventos:

  • Invitacion publicada (PUBLISHED): Si el evento esta en DRAFT, automaticamente transiciona a ACTIVE via EventLifecycleService.activateEventOnPublish().
  • Evento cancelado (CANCELLED): Todas las invitaciones en UNPUBLISHED o PUBLISHED se cierran automaticamente (CLOSED).
  • Evento completado (COMPLETED): Cron job diario (medianoche, timezone America/Mexico_City) cierra invitaciones de eventos cuya fecha ya paso.

2. Tipos de Invitacion (InvitationSource)

Nvito soporta dos origenes de invitacion, discriminados por el campo source del modelo Invitation:

SourceDescripcionCreacionEdicionVersionamiento
INTERNAL (default)Invitacion creada con el motor de templates y IA de NvitoTemplate + IA + Editor Visual ArtisanEditor visual, secciones, IA conversacionalSi (max 50 versiones)
EXTERNALHTML disenado fuera de Nvito y cargado manualmenteUpload de archivo HTMLRe-upload completo del HTMLNo (siempre version 1)

Las invitaciones externas comparten la misma maquina de estados, el mismo sistema de publicacion/preview, las mismas analiticas y el mismo renderizado en nvito-invitations. La diferencia radica en como se crea y edita el contenido.


3. Flujo Completo — Invitaciones Internas (INTERNAL)

El recorrido end-to-end de una invitacion interna, desde su creacion hasta que un invitado la visualiza:

Paso 1: Creacion de la Invitacion

El usuario crea una invitacion asociada a un evento existente. Puede elegir entre:

  • Seleccionar un template: Se obtiene del catalogo via ArtisanTemplateRegistryService.getCatalog(). El template provee colores, fuentes y secciones predeterminadas.
  • Empezar desde cero: Se usa el schema por defecto del tipo de evento (event.eventType.defaultSections).

Restriccion: Maximo 3 invitaciones por evento. Se genera un slug unico con formato {eventSlug}-inv-{randomSuffix}.

Endpoint: POST /invitations - Requiere permiso invitations:create:assigned.

Paso 2: Generacion con IA (Opcional)

El usuario puede utilizar la generacion con IA para crear o modificar el contenido de la invitacion a partir de lenguaje natural. El servicio AiGenerationService genera el schema y theme correspondientes, creando una nueva version en el proceso.

Paso 3: Edicion con Editor Visual Artisan

El usuario edita la invitacion en el editor visual de nvito-admin:

  • Modifica secciones, textos, imagenes, colores y fuentes.
  • Reordena secciones con drag & drop.
  • Selecciona variantes visuales para cada seccion.
  • Previsualiza en tiempo real via POST /invitations/:id/render-draft (compilacion sin persistencia a CDN).

Endpoint: PATCH /invitations/:id - Actualiza schema y/o theme.

Paso 4: Secciones y Variantes

Cada seccion puede configurarse con una variante visual diferente. El usuario selecciona variantes desde el catalogo de section-variants/ y la configuracion se guarda en schema.sectionConfig[sectionType].variantId.

Paso 5: Preview

El usuario genera un token temporal de preview (JWT, 15 minutos de validez) via POST /invitations/:id/preview-token. Este token permite visualizar la invitacion compilada sin necesidad de publicarla, accesible via GET /invitations/preview/:token.

Paso 6: Publicacion

Al aprobar la invitacion (POST /invitations/:id/approve):

  1. Se compila el schema a HTML/CSS/JS via TemplateCompilerService.
  2. Se optimiza y agregan meta tags SEO via HtmlOptimizerService.
  3. Se sube a CDN (R2/MinIO) via StorageService.
  4. Se actualizan htmlUrl, cssUrl, jsUrl en la BD.
  5. Se envia webhook de revalidacion a nvito-invitations para ISR.
  6. Si el evento esta en DRAFT, se activa automaticamente.

Paso 7: Visualizacion por Invitados

El invitado recibe un enlace con formato https://nvito.mx/i/{slug}. La aplicacion nvito-invitations (Next.js) renderiza el micrositio usando ISR (Incremental Static Regeneration):

  1. Consulta GET /invitations/public/:slug para obtener las URLs del HTML.
  2. Renderiza el micrositio completo con las secciones configuradas.
  3. Registra eventos de analiticas (VIEW) en la API.

Paso 8: RSVP del Invitado

El invitado interactua con la seccion RSVP integrada en el micrositio. Al confirmar o declinar, se registra el RSVP en la API y se dispara un evento de analitica RSVP_SUBMIT.


4. Flujo Completo — Invitaciones Externas (EXTERNAL)

Las invitaciones externas permiten a usuarios con disenos HTML propios (ej. disenadores, agencias) publicar sus invitaciones a traves de la plataforma Nvito, aprovechando el hosting, las analiticas, el sistema RSVP y la distribucion via comunicaciones.

4.1 Diagrama de Secuencia

4.2 Creacion

El usuario sube un archivo HTML (.html / .htm) desde nvito-admin. El archivo se envia como string al endpoint POST /invitations/external.

Proceso de creacion:

  1. Se verifica que el evento exista y pertenezca a la organizacion del usuario.
  2. Se verifica el limite de 3 invitaciones por evento.
  3. Se valida el HTML con HtmlValidatorService (ver seccion 4.4).
  4. Se genera un slug temporal con formato {eventSlug}-ext-{random6chars}.
  5. Se crea el registro en BD con source: 'EXTERNAL', status: 'IN_CONSTRUCTION', schema: {}, theme: {}.
  6. Se sube el HTML a CDN (R2/MinIO) via StorageService.
  7. Se guarda un backup del HTML raw en el campo externalHtmlRaw de la BD.
  8. Se retorna la invitacion creada junto con warnings (si los hay).

Campos opcionales: seoTitle (max 200 chars) y seoDescription (max 500 chars) para personalizar los meta tags SEO.

4.3 Restricciones

Las invitaciones externas tienen las siguientes restricciones respecto a las internas:

FuncionalidadInternaExterna
Editor Visual ArtisanSiNo
Generacion/modificacion con IASiNo
Secciones editablesSiNo
Versionamiento (rollback)Si (max 50)No
Edicion de schema/themeSi (PATCH /invitations/:id)No (lanza error)
Re-upload de HTMLNoSi (solo en IN_CONSTRUCTION)
Descarga del HTML originalNoSi
Preview con tokenSiSi
PublicacionSi (genera HTML)Si (usa HTML existente)
AnaliticasSiSi
RSVPSiSi (si el HTML incluye formulario RSVP)
ComunicacionesSiSi

4.4 Validacion de HTML (HtmlValidatorService)

El servicio HtmlValidatorService ejecuta un pipeline de 9 pasos de validacion que retorna { valid: boolean, errors: string[], warnings: string[] }:

PasoTipoDescripcion
1. TamanoErrorBloquea si el HTML supera 5 MB
2. EstructuraErrorRequiere <!DOCTYPE html> o <html> + etiqueta <body>
3. Scripts peligrososErrorBloquea: eval(), new Function(), document.cookie, document.domain, window.location =, XMLHttpRequest, WebSocket(), importScripts(), navigator.sendBeacon, fetch()
4. IframesErrorSolo permitidos desde dominios de whitelist (Google, YouTube, Vimeo, Spotify, Maps)
5. Formularios externosError<form action="http(s)://..."> con URL absoluta externa bloqueado
6. Meta refreshError<meta http-equiv="refresh"> bloqueado
7. Event handlersErroronerror/onload con eval, fetch, document.cookie, window.location bloqueado
8. URLs relativasWarningAdvierte sobre src/href con paths relativos (que no funcionaran)
9. ViewportWarningAdvierte si falta <meta name="viewport">

Los errores bloquean la creacion y retornan 400. Los warnings no bloquean pero se informan al usuario.

4.5 Re-upload

El usuario puede reemplazar el HTML de una invitacion externa que este en estado IN_CONSTRUCTION. El HTML anterior se sobreescribe de forma irreversible.

Restricciones del re-upload:

  • Solo invitaciones con source: 'EXTERNAL'
  • Solo en estado IN_CONSTRUCTION (si esta publicada, debe despublicarse primero)
  • El nuevo HTML pasa por la misma validacion de 9 pasos

Endpoint: PUT /invitations/:id/external/reupload

4.6 Descarga del HTML Original

El usuario puede descargar el HTML original que subio, almacenado en el campo externalHtmlRaw de la BD (no requiere acceso a S3).

Endpoint: GET /invitations/:id/external/download

4.7 Publicacion de Externas

La publicacion de invitaciones externas usa el mismo flujo de aprobacion (POST /invitations/:id/approve), pero con diferencias:

  1. No se genera HTML: el HTML ya existe en CDN (subido en el paso de creacion/re-upload).
  2. No se validan secciones: solo se verifica que htmlUrl no sea nulo.
  3. Se genera slug amigable: si el evento tiene un custom domain activo y verificado, el slug se actualiza con el path del custom domain (ej. boda-real-2026). Si no, se usa el nombre del evento slugificado.
  4. Se activa el evento si esta en DRAFT (igual que internas).
  5. Se envia webhook ISR a nvito-invitations para revalidar la pagina.

4.8 Endpoints de Invitaciones Externas

MetodoEndpointDescripcionPermiso
POST/invitations/externalSubir HTML y crear invitacion externainvitations:create:assigned
PUT/invitations/:id/external/reuploadReemplazar HTML de invitacion externainvitations:update
GET/invitations/:id/external/downloadDescargar HTML originalAutenticado

5. Templates

5.1 Catalogo de Templates

Los templates son plantillas prediseadas almacenadas en la base de datos (tabla templates) con estado ACTIVE. Al iniciar el modulo, ArtisanTemplateRegistryService carga todos los templates activos en un cache en memoria.

Cada template esta asociado a un tipo de evento y provee una configuracion completa:

CampoDescripcion
slugIdentificador unico (e.g., wedding-classic, xv-princess)
eventTypeTipo de evento asociado (e.g., wedding, graduation, birthday)
configObjeto JSON con colores, fuentes, secciones y orden
previewImageUrlURL de imagen de preview para el catalogo
tagsEtiquetas para filtrado y busqueda

5.2 Templates por Defecto por Tipo de Evento

Tipo de EventoTemplate por Defecto
Weddingwedding-classic
XV Anosxv-princess
Birthdaybirthday-festive
Baptismbaptism-celestial
Communioncommunion-serene
Baby Showerbabyshower-sweet
Graduationgraduation-classic
Corporatecorporate-clean
Otherother-versatile

5.3 Estructura del Template (TemplateMeta)

Cada template expone: id (slug), eventType, name, description, previewImageUrl, tags, defaultColors (primary, secondary, accent, background, text), defaultFonts (heading, body), defaultSections (visibles por defecto) y optionalSections (ocultas por defecto).

5.4 Compilacion de Templates

El TemplateBuilderService construye el HTML a partir de la config: lee secciones, selecciona variantes del registro section-variants/, ensambla HTML con placeholders Handlebars ({{variable}}), y cachea en ArtisanTemplateRegistryService.

Endpoints publicos: GET /invitations/templates (catalogo filtrable por eventType), GET /invitations/templates/:templateId (metadata), GET /invitations/variants (variantes por tipo).


6. Secciones

6.1 Tipos de Seccion Disponibles

El sistema define 18 tipos de seccion con 121 variantes visuales en total:

hero (13 variantes), welcome (9), event_details (7), countdown (8), story (8), gallery (11), itinerary (5), location (6), rsvp (8), registry (5), accommodation (5), faq (5), music (5), sponsors (5), prayer (5), thank_you (5), dress_code (5), footer (6).

6.2 Estructura de una Variante de Seccion

interface SectionVariant {
  id: string;          // e.g., "hero-centered-overlay"
  sectionType: string; // e.g., "hero"
  name: string;        // Nombre legible en espanol
  description: string; // Descripcion corta
  css: string;         // CSS especifico de la variante
  html: string;        // HTML con placeholders {{}} Handlebars
}

6.3 Ordenamiento y Visibilidad

  • Orden: El schema almacena un array sectionOrder que define el orden de renderizado. El usuario puede reordenar secciones con drag & drop en el editor visual.
  • Visibilidad: El objeto sectionVisibility del schema controla que secciones se muestran (true) o se ocultan (false).
  • Configuracion por seccion: sectionConfig[sectionType] almacena configuracion especifica, incluyendo variantId para override de la variante visual.

6.4 Resolucion de Variantes

Cuando se compila una invitacion, el HtmlGeneratorService resuelve las variantes para cada seccion visible:

  1. Si el usuario selecciono un variantId en sectionConfig, se usa esa variante.
  2. Si la seccion existe en el template pero el usuario cambio la variante, se reemplaza el bloque HTML/CSS.
  3. Si la seccion no existe en el template, se inyecta la variante seleccionada (o la primera disponible).

7. Versionamiento

7.1 Modelo InvitationVersion

Cada cambio significativo en una invitacion genera una version (snapshot). El servicio VersionManagerService gestiona el ciclo de vida de las versiones. Campos principales: id (UUID), invitationId, versionNumber (secuencial), schema (JSON), theme (JSON), changeType (enum: ai_generation | ai_modify | manual_edit | rollback), changePrompt, createdBy, createdAt, htmlUrl, cssUrl, jsUrl.

7.2 Creacion de Versiones

Las versiones se crean en los siguientes escenarios:

  • Edicion manual (manual_edit): Cuando el usuario guarda cambios desde el editor visual.
  • Generacion con IA (ai_generation): Cuando se genera contenido nuevo con IA.
  • Modificacion con IA (ai_modify): Cuando se modifica contenido existente con IA.
  • Rollback (rollback): Cuando se restaura una version anterior.

Limite: Maximo 50 versiones por invitacion. Al exceder el limite, se elimina la version mas antigua automaticamente.

7.3 Rollback a Version Anterior

El proceso de rollback (POST /invitations/:id/versions/:versionNumber/rollback):

  1. Se obtiene la version objetivo por numero.
  2. Se actualiza el schema y theme de la invitacion con los datos de la version objetivo.
  3. Se crea una nueva version de tipo rollback que registra la restauracion.
  4. Se retorna la invitacion actualizada y la nueva version creada.

7.4 Comparacion y Endpoints

El endpoint GET /invitations/:id/versions/compare?version1=X&version2=Y compara dos versiones aplanando claves JSON y reportando campos agregados, eliminados y modificados.

Endpoints: POST /versions (crear), GET /versions (historial), GET /versions/stats, GET /versions/:n (especifica), POST /versions/:n/rollback, GET /versions/compare.


8. Generacion HTML

8.1 Pipeline de Compilacion

El proceso de compilacion transforma el schema JSON de una invitacion en HTML/CSS/JS optimizado y listo para produccion. Los servicios involucrados son:

ServicioResponsabilidad
HtmlGeneratorServiceOrquestador principal del pipeline
ArtisanTemplateRegistryServiceProvee el HTML base del template
TemplateCompilerServiceCompila placeholders Handlebars con datos reales
HtmlOptimizerServiceOptimiza bundle y agrega meta tags SEO
StorageServiceSube archivos a CDN (R2/MinIO)
WebhookServiceNotifica a nvito-invitations para revalidacion ISR

8.2 Proceso de Compilacion Paso a Paso

  1. Obtener schema y theme de la invitacion (o de una version especifica).
  2. Resolver template: Obtener el HTML base del registro via templateId del schema.
  3. Construir TemplateData: Combinar datos del schema con colores, fuentes y metadatos.
  4. Calcular secciones visibles: Aplicar sectionOrder y sectionVisibility.
  5. Resolver variantes: Reemplazar bloques de seccion con variantes seleccionadas por el usuario.
  6. Compilar Handlebars: Reemplazar todos los placeholders {{variable}} con datos reales.
  7. Optimizar: Minificar, agregar meta tags SEO, Open Graph, etc.
  8. Subir a CDN: Persistir index.html, styles.css y scripts.js en R2/MinIO.
  9. Actualizar BD: Guardar htmlUrl, cssUrl, jsUrl y htmlGeneratedAt en la invitacion.
  10. Webhook ISR: Notificar a nvito-invitations para revalidar la pagina.

8.3 Modos de Compilacion

ModoEndpointPersistenciaUso
DraftPOST /invitations/:id/render-draftNo (solo respuesta)Editor visual, preview en tiempo real via iframe
GeneratePOST /invitations/:id/generate-htmlSi (CDN + BD)Generacion independiente sin cambiar estado
PublishPOST /invitations/:id/publish-htmlSi (CDN + BD + estado)Generacion + publicacion en un solo paso

8.4 Diagrama de Publicacion (Internas)

8.5 Rutas de Almacenamiento en CDN

Los archivos se almacenan con la siguiente estructura en R2/MinIO:

/{organizationId}/{eventId}/{invitationId}/v{version}/index.html
/{organizationId}/{eventId}/{invitationId}/v{version}/styles.css
/{organizationId}/{eventId}/{invitationId}/v{version}/scripts.js

8.6 Custom Domains

El WebhookService soporta dominios personalizados. Al publicar, busca CustomDomain activo y verificado del evento, construye paths de revalidacion (/i/{slug} + /d/{customDomain}), y envia webhook con header X-Revalidate-Secret.


9. Analiticas

9.1 Modelo de Eventos

El sistema de analiticas rastrea la interaccion de los invitados con las invitaciones publicadas. Los eventos se almacenan en InvitationAnalyticsEvent:

Tipo de EventoDescripcion
VIEWEl invitado visualizo la invitacion
RSVP_OPENEl invitado abrio el formulario de RSVP
RSVP_SUBMITEl invitado envio su respuesta RSVP
LINK_CLICKEl invitado hizo clic en un enlace (mesa de regalos, ubicacion, etc.)
SHAREEl invitado compartio la invitacion
DOWNLOADEl invitado descargo contenido de la invitacion

9.2 Datos Capturados por Evento

Cada evento registra: invitationId, eventType, visitorId (anonimo via cookie/fingerprint), ipAddress, userAgent, country (ISO 2 letras), city, device (mobile/desktop/tablet), browser, os, referrer y metadata (JSON adicional).

9.3 Resumen Agregado (InvitationAnalyticsSummary)

Para cada invitacion se mantiene un resumen agregado que se actualiza en tiempo real (fire-and-forget) despues de cada evento:

MetricaDescripcion
totalViewsTotal de visualizaciones
uniqueVisitorsVisitantes unicos (por visitorId distinto)
rsvpOpensVeces que se abrio el formulario RSVP
rsvpSubmitsRespuestas RSVP enviadas
linkClicksClics en enlaces
sharesVeces compartida
downloadsDescargas realizadas
conversionRateTasa de conversion: (rsvpSubmits / totalViews) * 100
deviceBreakdownDistribucion por tipo de dispositivo (JSON)
countryBreakdownDistribucion por pais (JSON)
topReferrersTop 10 origenes de trafico (JSON)

9.4 Flujo de Tracking

  1. nvito-invitations (frontend publico) detecta la interaccion del invitado.
  2. Envia un POST /events/:eventId/analytics/track con los datos del evento (endpoint publico, sin autenticacion).
  3. nvito-api crea el registro en InvitationAnalyticsEvent.
  4. Asincrona y de forma no bloqueante (fire-and-forget), actualiza el InvitationAnalyticsSummary:
    • Incrementa el contador correspondiente.
    • Recalcula visitantes unicos si es un evento VIEW.
    • Recalcula metricas agregadas (conversion rate, desglose por dispositivo, pais y referrers).

9.5 Endpoints de Analiticas

EndpointMetodoAutenticacion
/events/:eventId/analytics/trackPOSTPublico
/events/:eventId/analytics/overviewGETowner/admin
/events/:eventId/analytics/guestsGETowner/admin
/events/:eventId/analytics/rsvpGETowner/admin
/events/:eventId/analytics/timelineGETowner/admin
/events/:eventId/analytics/invitations/:id/summaryGETowner/admin
/events/:eventId/analytics/invitations/:id/detailedGETowner/admin

9.6 Analiticas a Nivel de Evento

El AnalyticsService provee analiticas agregadas a nivel de evento combinando datos de todas las invitaciones: overview (invitados, RSVP, mesas, QR), guest analytics (por grupo y estado RSVP), RSVP timeline (ultimos 30 dias), y actividad reciente (audit logs con fallback a consulta directa).

Referencias

Archivos fuente principales: invitations.service.ts, invitations.controller.ts, external-invitation.controller.ts, external-invitation.service.ts, html-validator.service.ts, approval-workflow.service.ts, version-manager.service.ts, html-generator.service.ts, template-compiler.service.ts, artisan-template-registry.service.ts, html-optimizer.service.ts, webhook.service.ts, analytics.service.ts, section-variants/index.ts. Documentacion relacionada: CICLO_VIDA_EVENTOS_INVITACIONES.md.

Esta pagina fue util?