Skip to main content
Traducción. Autoritativo: fs-core-0004-consents.md.

Contexto

La Ley 21.719 entra en plena vigencia el 2026-12-01, y el punto que da forma a este diseño es que fiscaliza evidencia operativa, no políticas. Una columna booleana que diga email_opt_in = true no prueba nada: no puede decir cuándo, cómo, ni qué se le mostró a la persona. Por eso el consentimiento es una entidad con historia. Cada otorgamiento y cada revocación es una fila con prueba y timestamp, y el estado actual se deriva de la última fila. Sobreescribir es lo que destruye la evidencia, así que sobreescribir está prohibido. Esto además tiene peso comercial directo: el consentimiento define el contacto accionable, que es la base facturable de toda la suite (ADR-014). Una revocación reduce la factura del ciclo siguiente sin ventana de castigo — esa es una promesa de precio implementada aquí.

Alcance (normativo)

  • core.consents: historia append-only por (contacto, canal, finalidad).
  • core.consent_purposes: paramétrica — transactional, marketing, product.
  • Captura de prueba: origen, IP, user agent, versión del texto mostrado, timestamp.
  • Resolución de estado actual: gana la última fila.
  • El predicado de contacto accionable que usan medición y mensajería.
  • Endpoints de otorgar y revocar, más una lectura de preferencias para el member.

Fuera de alcance (normativo)

  • Decidir si enviar. messaging lee el consentimiento; la decisión vive en resolveChannels.
  • Las supresiones, que son otra cosa: el consentimiento es lo que la persona eligió, una supresión es lo que el proveedor o el sistema impusieron tras un rebote duro. Nunca deben confundirse.
  • La UI del centro de preferencias — frontend/portal.

Comportamiento (normativo)

  1. El consentimiento es append-only. Una revocación es una fila nueva, nunca un update. PROHIBIDO: un booleano mutable en cualquier parte que represente consentimiento.
  2. Toda fila lleva prueba: origen, timestamp de captura e identificador de la versión del texto que se mostró. Consentimiento sin prueba no es consentimiento.
  3. El estado actual por (contacto, canal, finalidad) es la fila con mayor captured_at. Los empates son imposibles: la columna es timestamptz con precisión de microsegundo más una secuencia monótona.
  4. La finalidad transaccional no es opt-out (DEC-E3). Quien compró algo recibe su comprobante. Solo una supresión por rebote duro lo detiene.
  5. La ausencia de consentimiento no es consentimiento. El estado por defecto para marketing es denegado, en todas partes, sin override por tenant.
  6. Una revocación surte efecto de inmediato — antes de la siguiente decisión de envío, no en el recálculo nocturno.
  7. Un contacto accionable es el que tiene un otorgamiento activo en ≥1 canal para una finalidad no transaccional, y no está suprimido. Ese predicado se define aquí una vez y lo consume la medición.
  8. El consentimiento sobrevive a una fusión: el superviviente hereda la unión, y donde ambos lados difieren gana lo más restrictivo. Heredar un otorgamiento que la persona nunca dio para ese registro es exactamente la falla que esta regla evita.
  9. La supresión borra las filas de consentimiento junto con el perfil, pero el registro agregado de prueba de tratamiento en la auditoría sobrevive (DEC-J3).

Datos (normativo)

API (normativo)

Eventos (normativo)

core.consent.granted y core.consent.revoked, ambos disponibles como webhooks salientes. Una revocación es de los pocos eventos que un tenant casi siempre quiere espejar en sus propios sistemas.

Criterios de aceptación (normativo)

  1. Otorgar, revocar y volver a otorgar produce tres filas, y el estado actual es otorgado.
  2. Ningún camino de código actualiza ni borra una fila de consentimiento.
  3. Una revocación se honra en la siguiente decisión de envío, verificado end to end.
  4. Un contacto sin fila de consentimiento no es accionable y no recibe mensajes de marketing.
  5. Los mensajes transaccionales llegan a un contacto que revocó marketing.
  6. Tras una fusión donde un lado otorgó y el otro revocó el mismo canal y finalidad, el resultado es revocado.
  7. El conteo de contactos accionables coincide con un conjunto de control calculado independientemente sobre una población sembrada de 50 000.
  8. Negativo: ningún endpoint ni configuración permite inscribir un contacto en marketing por defecto.

Ejecución

Un solo slice, comando síncrono. El predicado de accionable vive en packages/core como función pura, para que medición y mensajería no puedan separarse en su interpretación.

Preguntas abiertas

Changelog

Registro de entrega

Aún no implementado.