Skip to main content
Traducción. Autoritativo: ../../standards/notifications.md.

Catálogo de canales (core.notification_channels, paramétrico)

email · in_app · webhook · push · sms (adaptador en F1b tras el ADR del proveedor) · whatsapp (F2) · live_activity (F2). La fila del canal y el NotificationChannelPort existen desde el día 1 aunque el adaptador llegue después.

Resolución en cascada (niveles 0–3 + destinatario)

resolveChannels(evento, tenant, módulo, destinatario) — función PURA en packages/core, con cobertura unitaria completa:
  1. Plataforma: el adaptador existe y está operativo (flag operacional de Vercel Flags, espejado en messaging.platform_channel_status).
  2. Tenant: canal activo para este tenant (entitlement + credenciales o dominio de envío configurados) — messaging.tenant_channel_settings.
  3. Módulo: canal habilitado para el módulo emisor en este tenant — messaging.module_channel_settings.
  4. Tipo de evento: entrada de enrutamiento (canales, plantilla por canal, categoría) — messaging.event_channel_routing.
Compuertas del destinatario: consentimiento para el canal ∧ no estar en messaging.suppressions ∧ centro de preferencias ∧ (solo marketing: quiet hours + frequency caps). Resultado: un job por canal resuelto en notif.{canal}.{carril} (standards/jobs.md).

Categorías y controles del destinatario

  • Categoría por enrutamiento de evento: transactional | marketing | product. El transaccional NO es opt-out (solo lo detiene una supresión por rebote duro).
  • Centro de preferencias del member: por canal Y por categoría. Quiet hours: default por tenant, override por campaña. Frequency caps: solo marketing.

Trazabilidad (end to end)

  • Cadena: evento de origen → regla o campaña → job de notificación → messaging.sendsmessaging.send_status_history (queued→sent→delivered→opened→clicked / bounced / complained / failed) → webhooks salientes. Todo unido por correlation_id + origin_event_id.
  • Ingesta de estados por webhooks del proveedor (hoy Resend); cada canal futuro trae su adaptador de estados.
  • Los rebotes duros y las quejas escriben automáticamente en messaging.suppressions. CADA envío verifica supresiones — sin excepciones.

Carriles y equidad

El carril transaccional siempre gana al de marketing (jobs.md). Token buckets por proveedor (global) y por tenant. Blasts: solo endpoints batch, con throttling; el blast de un tenant NO DEBE degradar la latencia de OTP de otro (esta es la mitigación del límite compartido de Resend, generalizada a todos los canales).