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 datosAntes 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 esclient, tu backend debe enviar la evidencia de que ya autenticaste al titular, y la API key necesitaconsent:identity-assert. Si esconsensa, 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.rutse pide cuando el perfil incluye cédula o el Flow contiene REDEC;emailcuando 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
- El titular puede revisar y revocar lo que otorgó desde el Centro de Privacidad.
- Tu backend puede revocar por API con
POST /v1/consents/{consentId}/revoke. - La Evidence es el hecho auditable, separado del estado vigente.