Developers

Flujo: consentimiento general

Integración completa de punta a punta, del Flow publicado hasta la autorización de uso.

Este es el recorrido completo de una integración de consentimiento general: seis pasos, con el request exacto de cada uno. El flujo REDEC reutiliza los cuatro primeros y agrega el gate regulatorio.

1. Backend  → GET  /v1/embed/flows/{configKey}        ¿qué pide este Flow?
2. Backend  → POST /v1/embed/sessions                 sesión + launchTicket
3. Frontend → <consensa-consent>.launch(ticket)       el titular decide
4. Frontend → eventos consensa:*                      feedback de interfaz
5. Backend  → GET  /v1/consent-interactions/{id}      resultado canónico
6. Backend  → POST /v1/authorization/check            justo antes de tratar los datos

Antes de empezar necesitas un Flow publicado en el Portal. Su "clave de integración" es el configKey que usarás en todo el flujo.

1. Lee qué pide el Flow

No hardcodees los requisitos del Flow: léelos. Esta operación te dice qué consentimientos presenta, qué política de autenticación tiene congelada y qué identificadores necesita el cliente.

curl "$CONSENSA_API_URL/v1/embed/flows/general-consent" \
  --header "Authorization: Bearer $CONSENSA_API_KEY" \
  --header "Origin: https://banco.ejemplo.cl"
{
  "configKey": "general-consent",
  "version": 5,
  "consents": [
    { "templateKey": "privacy", "title": "Tratamiento de datos personales", "kind": "general" },
    { "templateKey": "marketing", "title": "Comunicaciones comerciales", "kind": "general" }
  ],
  "containsRedec": false,
  "authentication": { "policyCode": "general_client_auth_v1", "source": "client", "profile": null, "mechanisms": [] },
  "requiredIdentifiers": [],
  "redecConfiguration": null,
  "origin": { "value": "https://banco.ejemplo.cl", "allowed": true }
}

Dos campos deciden cómo armas el paso siguiente:

  • authentication.source. Si es client, tu backend debe enviar la evidencia de que ya autenticaste al titular, y la API key necesita consent:identity-assert. Si es consensa, envías sólo { "source": "consensa" } y el runtime se encarga de verificar la identidad.
  • requiredIdentifiers. Por cada valor de esa lista debes enviar un identifier. rut se pide cuando el perfil incluye cédula o el Flow contiene REDEC; email cuando el perfil usa OTP por correo o cuando un Flow REDEC delega la notificación en Consensa.

origin.allowed es informativo. El enforcement real ocurre al crear la sesión.

2. Crea la sesión desde tu backend

curl --request POST "$CONSENSA_API_URL/v1/embed/sessions" \
  --header "Authorization: Bearer $CONSENSA_API_KEY" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: embed-customer-4821-01" \
  --data '{
    "configKey": "general-consent",
    "userReference": "customer-4821",
    "origin": "https://banco.ejemplo.cl",
    "authentication": {
      "source": "client",
      "method": "bank_session",
      "authenticatedAt": 1787702400000,
      "reference": "session-reference-8f2a"
    }
  }'

No hace falta resolver al cliente por separado: userReference lo resuelve o lo crea en línea. Si prefieres tener el customerId de antemano, usa POST /v1/customers/resolve.

origin debe coincidir exactamente con un origen permitido y activo del tenant. Nunca se infiere del request.

La respuesta tiene dos formas, y presentation.required es el campo que las distingue.

Caso A — presentation.required: false

{ "presentation": { "required": false, "reason": "all_satisfied" }, "replayed": false }

El titular ya satisface todo lo que el Flow presenta. No montes el componente: no se creó sesión ni interacción. Sigue tu flujo de negocio y, antes de tratar los datos, salta al paso 6.

Caso B — presentation.required: true

{
  "presentation": { "required": true },
  "sessionId": "embs_6f0b…",
  "interactionId": "int_9a3f…",
  "status": "pending",
  "launchTicket": "elt_…",
  "launchExpiresAt": 1787702460000,
  "expiresAt": 1787703000000,
  "authentication": { "source": "client", "status": "verified" },
  "replayed": false
}

Guarda el interactionId en tu backend: es lo que vas a consultar en el paso 5.

El launchTicket no es una credencial de sesión: es un ticket de un solo uso que vence a los 60 segundos. Pídelo justo antes de montar el componente, no lo registres en logs, no lo persistas y nunca lo pongas en un atributo HTML ni en una URL.

3. Monta el Web Component

Carga el bundle host, crea el elemento con la URL de runtime del ambiente y el sessionId (que no es secreto), y entrega el ticket con launch().

<script type="module" src="https://embed.consensa.example/host.js"></script>
<div id="consent-slot"></div>

<script type="module">
  const consent = document.createElement('consensa-consent');
  consent.setAttribute('title', 'Consentimiento para solicitud de crédito');
  consent.setAttribute('src', 'https://embed.consensa.example/runtime.html');
  consent.setAttribute('session', sessionId);
  document.querySelector('#consent-slot').replaceChildren(consent);
  consent.launch(launchTicket);
</script>

El componente monta un iframe de Consensa cross-origin y le pasa el ticket por postMessage sólo después de que el runtime se anuncia. El runtime lo canjea una única vez, ligado al origen de tu página que certifica el navegador, por la credencial real de la sesión. Esa credencial vive sólo dentro del iframe: ni tu código, ni scripts de terceros, ni herramientas de session replay pueden leerla.

Si tu página no corre en el origen con el que creaste la sesión, el canje falla con ORIGIN_NOT_ALLOWED y el componente muestra el estado inválido.

4. Escucha los eventos

Los eventos son notificaciones sanitizadas para la interfaz. No contienen tickets, credenciales, sessionId, identificadores personales ni IDs internos.

const log = ({ type, detail }) => console.log(type, detail.interactionReference);

consent.addEventListener('consensa:ready', log);       // el runtime cargó
consent.addEventListener('consensa:granted', log);     // un consentimiento, otorgado
consent.addEventListener('consensa:denied', log);      // un consentimiento, denegado
consent.addEventListener('consensa:completed', log);   // dos o más consentimientos
consent.addEventListener('consensa:cancelled', log);   // el titular salió sin decidir
consent.addEventListener('consensa:expired', log);     // venció el ticket o la sesión
consent.addEventListener('consensa:error', log);       // error sanitizado, con `code`

consent.addEventListener('consensa:granted', () => mostrarSiguientePaso());

Con dos o más consentimientos sólo se emite consensa:completed, con el detalle por templateKey. Ver Multiconsentimiento.

Si recibes consensa:expired, crea una sesión nueva desde tu backend.

5. Confirma el resultado canónico

Éste es el paso que decide, no el evento del navegador.

curl "$CONSENSA_API_URL/v1/consent-interactions/int_9a3f" \
  --header "Authorization: Bearer $CONSENSA_API_KEY"
{
  "interactionId": "int_9a3f",
  "status": "completed",
  "committedAt": 1787702455200,
  "consents": [
    { "templateKey": "privacy",   "kind": "general", "decision": "grant", "status": "granted" },
    { "templateKey": "marketing", "kind": "general", "decision": "deny",  "status": "denied"  }
  ]
}

Cada consentimiento incluido recibe una decisión explícita. optional no significa omitido ni denegación implícita, y required no significa aceptación forzada: el titular siempre puede denegar.

Si status es pending o expired, no hay decisión que honrar.

6. Autoriza justo antes de tratar los datos

Entre el consentimiento y el tratamiento puede pasar cualquier cosa: el titular revoca, el consentimiento expira, la configuración cambia. Por eso la autorización se consulta inmediatamente antes de la operación, no al principio del flujo.

curl --request POST "$CONSENSA_API_URL/v1/authorization/check" \
  --header "Authorization: Bearer $CONSENSA_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{ "userReference": "customer-4821", "configKey": "credit-evaluation" }'
{ "decision": "ALLOW", "configKey": "credit-evaluation", "validUntil": 1790294400000 }

Continúa sólo ante un ALLOW explícito y no caches la autorización más allá de validUntil. Ver Autorización para el vocabulario de denegación y la restricción de Flows no ambiguos.

Después

En esta página