> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gu1.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Sesión biométrica embebida

> Inicia una sesión de reautenticación biométrica hospedada tras un KYC aprobado: URL lista para iframe, webhooks y veredicto final de Gu1.

## Resumen

**Biometría embebida** permite re-verificar que quien completa un flujo es la misma persona que aprobó el KYC. A diferencia del [chequeo biométrico síncrono](/es/use-cases/kyc/biometric), acá Gu1 devuelve un `sessionUrl` con UI hospedada de captura. Tu app lo embebe en un iframe (o redirige) para que el liveness ocurra en un entorno controlado.

<Warning>
  **Usá `status`** de la API o del webhook como resultado biométrico. Gu1 gestiona el ciclo completo de la sesión y puede devolver `rejected` tras políticas de la organización (cruces entre entidades, umbrales de face match).
</Warning>

## Requisitos

1. **KYC aprobado en Gu1** para la entidad persona.
2. **Retrato de referencia** disponible desde ese KYC.
3. **KYC habilitado** en tu organización (misma API key y permisos que la validación por sesión). La configuración interna de captura biométrica embebida la provisiona el equipo Gu1; como integrador no configurás workflows ni credenciales adicionales. Si la org aún no fue habilitada, `POST /api/kyc/biometric/sessions` puede responder `BIOMETRIC_WORKFLOW_NOT_CONFIGURED` (400) — contactá a tu contacto Gu1.
4. **Gu1 Biometría** activo para tu organización (`global_gueno_biometric_kyc`). Si no está habilitado, la API responde `NOT_ENABLED` (403) — solicitá la activación a Gu1.

## Crear sesión

`POST /api/kyc/biometric/sessions`

Parámetros clave: **`entityId`**, **`entityExternalId`** o **`entityTaxId`** (exactamente uno requerido), `webhookUrl` (opcional), `callback`, `language`. Flujo **face match** (liveness + comparación contra retrato del KYC aprobado; `NO_PORTRAIT` si falta selfie).

<Info>
  **Vista previa sandbox (solo GET):** `GET /api/entities/by-tax-id/{taxId}` puede devolver una persona **sintética** (`sandboxMock: true`, `id: null`) para números de prueba del catálogo cuando no hay fila real. Es **solo lectura** — para `POST /sessions` necesitás una entidad persistida.
</Info>

Respuesta `201`: `id`, `sessionUrl`, `iframeAllow`, `hostedSessionId`, `status` (`pending` en flujo real; `approved` / `rejected` inmediato en mock sandbox). Ver [mock sandbox](/es/use-cases/kyc/sandbox-mock-data#biometría-embebida-mock-sandbox).

## Flujo recomendado de integración

1. Entidad persona con **KYC `approved`**.
2. **`POST /api/kyc/biometric/sessions` una vez** por intención del usuario. Guardá `id`, `sessionUrl` y `hostedSessionId`.
3. Si responde **`409 ACTIVE_SESSION_EXISTS`**, cancelá con `POST .../sessions/{activeSessionId}/cancel` y recién ahí creá de nuevo (no reintentar en loop sin manejar el 409).
4. Si `status` es **`pending`**, embebé `sessionUrl` en iframe (o redirigí).
5. Esperá webhooks **`biometric.session_*`** (o `GET .../sessions/:id` / `POST .../sync` si hace falta).
6. Usá **`status`** y `rejectionCode` como resultado final.

<Warning>
  **No** uses reintentos repetidos de `POST /sessions` como flujo normal. Tras timeout o doble click pueden aparecer casos borde de idempotencia. Siempre manejá `409`, cancelá sesiones activas explícitamente y preferí webhooks sobre polling agresivo.
</Warning>

## Errores al crear (`POST /sessions`)

| HTTP | `error`                        | Cuándo                                             | Qué hacer                                               |
| ---- | ------------------------------ | -------------------------------------------------- | ------------------------------------------------------- |
| 400  | `NO_KYC`                       | No hay KYC aprobado                                | Completar KYC primero                                   |
| 400  | `NO_PORTRAIT`                  | KYC aprobado sin selfie de referencia (flujo real) | Repetir KYC con captura válida o usar mock sandbox      |
| 400  | `KYC_NOT_CONFIGURED`           | Credenciales KYC faltantes                         | Configurar KYC en la org                                |
| 400  | `ENTITY_NOT_FOUND`             | `entityId` inválido u org incorrecta               | Corregir entidad / `X-Organization-ID`                  |
| 400  | `INVALID_ENTITY_TYPE`          | Entidad no es `person`                             | Usar entidad persona                                    |
| 403  | `NOT_ENABLED`                  | Biometría no activa en marketplace                 | Pedir activación a Gu1                                  |
| 402  | `INSUFFICIENT_CREDITS_FOR_KYC` | Sin créditos/pack                                  | Recargar billing                                        |
| 409  | `ACTIVE_SESSION_EXISTS`        | Última sesión `pending` / `in_progress`            | Cancelar `activeSessionId` y crear de nuevo             |
| 502  | `PROVIDER_ERROR`               | Gu1 no pudo iniciar la captura hospedada           | Reintentar más tarde; usar código `error` y HTTP status |
| 500  | `CREATION_FAILED`              | Fallo inesperado al persistir                      | Revisar logs; cancelar sesión colgada; un reintento     |

Ejemplo **`409 ACTIVE_SESSION_EXISTS`** (aditivo — clientes que ignoran campos extra no cambian):

```json theme={null}
{
  "error": "ACTIVE_SESSION_EXISTS",
  "message": "Cannot create new biometric session. There is already a pending session for this entity. Please cancel or complete the existing session first.",
  "activeSessionId": "7618e10c-b1b1-408b-b361-1901077ced73"
}
```

Repetir `POST /sessions` con una sesión aún abierta **siempre** devuelve este `409`. Gu1 **no** responde `201` con la misma sesión `pending`.

## Sesiones previas y “actual”

Una entidad puede tener **varias** sesiones biométricas (aprobadas, rechazadas, canceladas).

| API                                                                                         | Significado                                                       |
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
| `GET .../entities/:entityId/current`                                                        | **Última creada** (cualquier estado) — recuperar iframe pendiente |
| `GET .../entities/by-tax-id/:taxId/current`                                                 | Igual, resuelto por `entityTaxId`                                 |
| `GET .../entities/by-external-id/:externalId/current`                                       | Igual, resuelto por `entityExternalId`                            |
| `GET .../sessions?entityId=...` (o `entityTaxId` / `entityExternalId`) → `currentSessionId` | Última sesión **`approved`** — verificación vigente               |
| Listado `data[]`                                                                            | Historial por `createdAt`                                         |

Una sesión **aprobada anterior** **no** bloquea crear otra. Solo bloquea una sesión **activa** (`pending` / `in_progress`) → `409`.

## `hostedSessionId`, cancelación y reintentos

`hostedSessionId` es la **referencia de sesión de captura hospedada** devuelta al crear. Gu1 la guarda de forma única para webhooks y sync.

**Recuperación correcta:** `409` → cancelar `activeSessionId` → un solo `POST` nuevo.

Tras cancelar, podés crear una sesión nueva. La persistencia idempotente (reutilizar una fila **terminal** existente si vuelve el mismo `hostedSessionId`) aplica solo después del cancel u otros outcomes terminales — **no** mientras la sesión siga `pending` o `in_progress`.

**Mock sandbox:** prefijo `sandbox-mock-bio-`; cada create mock genera un ID nuevo.

## Embeber

```html theme={null}
<iframe
  src="{{ sessionUrl }}"
  style="width: 100%; height: 700px; border: none;"
  allow="{{ iframeAllow }}"
></iframe>
```

## Consultar estado

* `GET /api/kyc/biometric/sessions/:id` — una sesión por ID
* `GET /api/kyc/biometric/entities/:entityId/current` — **sesión actual** = la más reciente por `createdAt`. Responde `200` con `null` si no hay sesiones.
* `GET /api/kyc/biometric/entities/by-tax-id/:taxId/current` — igual, resuelto por tax ID
* `GET /api/kyc/biometric/entities/by-external-id/:externalId/current` — igual, resuelto por external ID
* `GET /api/kyc/biometric/sessions?entityId=...` — listado; también acepta `entityTaxId` o `entityExternalId` (uno a la vez). `currentSessionId` es la última sesión **`approved`**.
* `POST /api/kyc/biometric/sessions/:id/sync`
* `POST /api/kyc/biometric/sessions/:id/cancel` — cancela manualmente sesiones `pending` o `in_progress` (marca `cancelled` en Gu1).
* `GET /api/kyc/biometric/sessions/:id/media?key=` — imágenes persistidas referenciadas en `decision`.

Si `POST /sessions` responde `409 ACTIVE_SESSION_EXISTS`, el cuerpo incluye `activeSessionId` para cancelar sin consultar `/current`.

<Note>
  La plataforma puede reportar **In Review** durante la revisión de la captura; en Gu1 eso se refleja como `in_progress` hasta el veredicto final (`approved` o `rejected`). Solo los estados terminales de resultado cuentan para definir la sesión actual.
</Note>

## Webhooks

* **`webhookUrl` por request** — si lo enviás al crear la sesión.
* **Webhooks de organización** — eventos `biometric.session_*`. Ver [eventos biométricos](/es/webhooks/events/biometric-events).

Para autenticación reforzada en login o step-up, preferí sesiones embebidas frente al endpoint síncrono.

## Sesiones mock en sandbox

En **sandbox**, si la entidad tiene un **KYC mock aprobado** (`taxId` de prueba como `99990001`, o KYC con `metadata.sandboxMock: true`), `POST /api/kyc/biometric/sessions` puede devolver un **resultado mock inmediato** (`approved` o `rejected`) sin captura hospedada.

* Por defecto: `99990001` → biometría **approved**; `99990011` → biometría **rejected** (KYC sigue approved).
* Override: `"metadata": { "sandboxMockOutcome": "rejected" }` en cualquier entidad elegible.
* Las respuestas mock usan `hostedSessionId` con prefijo `sandbox-mock-bio-` y pueden traer `status: approved` o `rejected` en el create (no solo `pending`).

Tablas y ejemplos completos: [Datos mock sandbox — Biometría embebida](/es/use-cases/kyc/sandbox-mock-data#biometría-embebida-mock-sandbox).
