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

# Factores de autenticación en la API

> Enrole y verifique factores en VU ONE FREE: OTP por correo, TOTP y enlace mágico. Incluye políticas, estados de factor, eventos y respuestas 402.

Esta página muestra cómo enrolar y verificar factores de autenticación con la API. Cubre el código por correo, el código TOTP y el enlace mágico. También explica las políticas de cada factor, los eventos de factores y qué pasa cuando un factor no está incluido en su plan. Al terminar, una cuenta de su tenant tiene un factor enrolado y verificado.

> [!WARNING]
>
> **VU ONE FREE es una versión limitada, en vista previa.** Solo la usan las cuentas de Identity Link que VU seleccionó hasta la disponibilidad general. Algunas funciones son del plan pago y las funciones, los límites y esta documentación pueden cambiar. [Vea qué incluye](https://docs.vu-one.com/vu-one/plan-free.md).

## Antes de empezar

Necesita un token válido. Obténgalo según [Autenticación con OAuth 2.0 y OIDC](https://docs.vu-one.com/vu-one/desarrolladores/autenticacion.md). Cada endpoint pide un permiso, por ejemplo `sys_factor_email:create` o `sys_factor:read`, salvo que el token tenga `role_admin`. Además, la cuenta debe existir. Cree la cuenta según [Cuentas, identidades y contraseñas](https://docs.vu-one.com/vu-one/desarrolladores/cuentas.md).

Defina estas variables antes de seguir los ejemplos:

```bash theme={null}
export TENANT_HOST="wispy-waterfall-3912.prod.vu-one.com"  # reemplace por el host de su tenant
export TOKEN="<access-token>"
export ACCOUNT_ID="<account-id>"
export BUSINESS_ID="<business-id>"
export CHANNEL_ID="<channel-id>"
```

## Flujo típico

Para el OTP por correo, el flujo tiene tres llamadas y una consulta de control:

| Paso | Llamada | Resultado |
| - | - | - |
| 1 | `POST /api/v1/email` | Enrola el factor en la cuenta. |
| 2 | `POST /api/v1/email/send` | Envía el código. Responde `201`. |
| 3 | `POST /api/v1/email/login` | Verifica el código. Responde `200` sin cuerpo. |
| 4 | `GET /api/v1/factors/account/{accountId}/active` | Confirma los factores activos de la cuenta. |

TOTP y enlace mágico tienen sus propios pasos y rutas. Las secciones siguientes muestran cada llamada completa.

## Factores incluidos en FREE

La tabla muestra el estado de cada factor en VU ONE FREE.

| Factor | Estado en FREE | Notas |
| - | - | - |
| Contraseña | Incluido | Sin flag adicional. |
| OTP por correo | Incluido | Tiene un límite de envíos por cuenta. Vea la sección de límites de envío. |
| TOTP | Incluido | Compatible con aplicaciones de autenticación. |
| Enlace mágico | Incluido | Necesita una política con URL de retorno. |
| OTP por SMS | Plan pago | La API responde `402`. |
| Push | Plan pago | La API responde `402`. |
| Sincronización | Plan pago | La API responde `402`. |
| Passkeys | No disponible | Esta vista previa no incluye passkeys ni WebAuthn. |

> [!NOTE]
>
> **OTP por SMS, Push y Sincronización: plan pago.** No se incluye en VU ONE FREE. En la consola se ve con el chip **Pago** y la API responde `402` con el código `4020`. [Compare los planes](https://docs.vu-one.com/vu-one/plan-free.md).

Para el cuerpo de la respuesta `402`, vea [Errores](https://docs.vu-one.com/vu-one/desarrolladores/errores.md#error-de-plan-402-y-404).

> [!NOTE]
>
> El factor facial usa un SDK. Durante la vista previa, los SDK no se distribuyen públicamente. Esta página no documenta ese flujo.

## OTP por correo

El código por correo se envía a la dirección de la cuenta o a una dirección que usted indique. Use este flujo en tres pasos.

**1. Enrole el factor**

Envíe un `POST` a `/api/v1/email`. Con `sendOtp` en `true`, la API envía un código en el mismo paso.

```bash theme={null}
curl -s -X POST "https://$TENANT_HOST/middleware/api/v1/email" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "'"$ACCOUNT_ID"'",
    "emailAddress": "<correo>",
    "businessId": "'"$BUSINESS_ID"'",
    "channelId": "'"$CHANNEL_ID"'",
    "sendOtp": false
  }' | jq .
```

La respuesta devuelve `accountId`, `channelId`, `emailAddress`, `enabled`, `locked` y `lastSuccessfulLogin`.

**2. Envíe el código**

Envíe un `POST` a `/api/v1/email/send`. El cuerpo usa los mismos campos que el paso anterior. Este endpoint no usa `sendOtp`. Indique en `emailAddress` la dirección que recibe el código.

```bash theme={null}
curl -s -X POST "https://$TENANT_HOST/middleware/api/v1/email/send" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "'"$ACCOUNT_ID"'",
    "emailAddress": "<correo>",
    "businessId": "'"$BUSINESS_ID"'",
    "channelId": "'"$CHANNEL_ID"'"
  }' | jq .
```

Una respuesta `201` confirma el envío. Si el envío falla, la API responde `502` con el código `507`.

**3. Verifique el código**

Envíe un `POST` a `/api/v1/email/login` con el código que recibió el usuario.

```bash theme={null}
curl -i -X POST "https://$TENANT_HOST/middleware/api/v1/email/login" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "'"$ACCOUNT_ID"'",
    "otpCode": "<codigo-recibido>",
    "channelId": "'"$CHANNEL_ID"'"
  }'
```

Si el código es correcto, la API responde `200` sin cuerpo. Si el código es incorrecto, responde `403` con el código `952`.

Si omite `businessId` o `channelId`, la API usa el negocio y el canal del contexto de la llamada: los encabezados `X-Business-Id` y `X-Channel-Id`, o los del token.

### Valores predeterminados de la política de correo

La política define estos valores. Los valores predeterminados aplican hasta que usted los cambie.

| Campo | Predeterminado | Rango permitido |
| - | - | - |
| Longitud del código | 6 | De 4 a 8 |
| Validez del código | 5 minutos | De 1 a 10 minutos |
| Intentos máximos | 3 | De 3 a 10 |
| Envíos por hora por cuenta | 5 | De 1 a 1000 |
| Bloqueo automático | 60 minutos | De 30 a 120 minutos |

## TOTP

TOTP usa una semilla que el usuario agrega a una aplicación de autenticación. El enrolamiento tiene dos pasos: primero se crea una sesión de enrolamiento y después se confirma con el primer código.

**1. Inicie el enrolamiento**

Envíe un `POST` a `/api/v1/otp/time/enrollments`.

```bash theme={null}
curl -s -X POST "https://$TENANT_HOST/middleware/api/v1/otp/time/enrollments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "'"$ACCOUNT_ID"'",
    "businessId": "'"$BUSINESS_ID"'",
    "channelId": "'"$CHANNEL_ID"'"
  }' | jq .
```

La respuesta devuelve `enrollmentId`, `accountId`, `seed`, `length`, `stepTime`, `expiresAt` y `temporal`. La sesión vence en 300 segundos de forma predeterminada.

> [!WARNING]
>
> La semilla (`seed`) da acceso al código TOTP. Muéstrela solo al usuario que la va a registrar. No la guarde en registros ni en logs.

**2. Confirme el enrolamiento con el primer código**

Envíe un `POST` a `/api/v1/otp/time/enrollments/{enrollmentId}/validate` con el código que muestra la aplicación.

```bash theme={null}
curl -s -X POST "https://$TENANT_HOST/middleware/api/v1/otp/time/enrollments/<enrollment-id>/validate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "'"$ACCOUNT_ID"'",
    "otp": "<codigo-de-la-aplicacion>"
  }' | jq .
```

La respuesta devuelve el factor TOTP activo, con campos como `id`, `enabled`, `locked`, `stepTime`, `length` y `activationState`.

**3. Verifique un código**

Envíe un `POST` a `/api/v1/otp/time/login`. La API responde `200` sin cuerpo si el código es correcto.

```bash theme={null}
curl -i -X POST "https://$TENANT_HOST/middleware/api/v1/otp/time/login" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "'"$ACCOUNT_ID"'",
    "otp": "<codigo-de-la-aplicacion>"
  }'
```

Si prefiere enviar el código QR al usuario por correo o SMS, use `POST /api/v1/otp/time/send`. El campo `mechanism` acepta `EMAIL` o `SMS`. El envío por SMS es parte del plan pago: en FREE, la API responde `402`. Para cancelar un enrolamiento pendiente, use `DELETE /api/v1/otp/time/enrollments/{enrollmentId}`.

> [!NOTE]
>
> **Envío del código QR por SMS: plan pago.** No se incluye en VU ONE FREE. En la consola se ve con el chip **Pago** y la API responde `402` con el código `4020`. [Compare los planes](https://docs.vu-one.com/vu-one/plan-free.md).

### Valores predeterminados de la política TOTP

| Campo | Predeterminado | Valores permitidos |
| - | - | - |
| Longitud del código | 6 | 4, 6 u 8 |
| Paso de tiempo | 30 segundos | 30, 60, 90 o 120 |
| Intentos máximos | 3 | De 3 a 10 |
| Ventana de validación | 1 paso | 0, 1 o 2 pasos |

## Enlace mágico

El enlace mágico envía un enlace al usuario. Al abrirlo, el navegador vuelve a la URL de retorno de la política con el estado de la transacción. Su aplicación cambia ese estado por una sesión.

Antes de empezar, configure la política con la URL de retorno (`defaultCallbackUrl`). Sin esa URL, el enlace no se puede consumir.

**1. Configure la política**

Envíe un `POST` a `/api/v1/policies/magic-link` con `businessId`, `channelId` y `defaultCallbackUrl`.

```bash theme={null}
curl -s -X POST "https://$TENANT_HOST/middleware/api/v1/policies/magic-link" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "businessId": "'"$BUSINESS_ID"'",
    "channelId": "'"$CHANNEL_ID"'",
    "defaultCallbackUrl": "https://<su-aplicacion>/magic-link/callback"
  }' | jq .
```

La vigencia del enlace se define en `defaultTtlInMinutes`. El valor predeterminado es 5 minutos. El rango permitido es de 1 a 1440 minutos.

**2. Cree el factor**

Envíe un `POST` a `/api/v1/otp/magic-link`.

```bash theme={null}
curl -s -X POST "https://$TENANT_HOST/middleware/api/v1/otp/magic-link" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "accountId": "'"$ACCOUNT_ID"'",
    "businessId": "'"$BUSINESS_ID"'",
    "channelId": "'"$CHANNEL_ID"'"
  }' | jq .
```

Guarde el `id` del factor. Lo necesita para enviar el enlace.

**3. Envíe el enlace**

Envíe un `POST` a `/api/v1/otp/magic-link/{factor_id}/transactions`. El cuerpo es opcional. Si omite `emailAddress`, la API usa el correo de la cuenta.

```bash theme={null}
curl -s -X POST "https://$TENANT_HOST/middleware/api/v1/otp/magic-link/<factor-id>/transactions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "emailAddress": "<correo>"
  }' | jq .
```

La respuesta, con código `201`, devuelve `id`, `factorId`, `accountId`, `status`, `registrationDate` y `expiresAt`. Guarde el `id` de la transacción.

En la plantilla del correo, el marcador `{{SIGNIN_URL}}` se reemplaza por el enlace. Las variables disponibles son `link`, `accountId` y `ttlMinutes`.

**4. El usuario abre el enlace**

El usuario abre el enlace del correo. Este paso no requiere token. El servidor responde `303` y redirige a su URL de retorno con dos parámetros: `status` y `transaction_id`.

```text theme={null}
https://<su-aplicacion>/magic-link/callback?status=consumed&transaction_id=<transaction-id>
```

El valor de `status` aparece en minúsculas. Un enlace consumido tiene el valor `consumed`.

**5. Obtenga la sesión**

Su backend recibe el `transaction_id` y llama a `POST /api/v1/magic-link/login`. Este endpoint no requiere token.

```bash theme={null}
curl -s -X POST "https://$TENANT_HOST/middleware/api/v1/magic-link/login" \
  -H "Content-Type: application/json" \
  -d '{
    "transactionId": "<transaction-id>",
    "businessId": "'"$BUSINESS_ID"'",
    "channelId": "'"$CHANNEL_ID"'"
  }' | jq .
```

Si el enlace es válido, la respuesta trae un token de acceso. Ese token tiene los mismos campos que la respuesta de [Autenticación con OAuth 2.0 y OIDC](https://docs.vu-one.com/vu-one/desarrolladores/autenticacion.md). Si su integración usa dispositivos registrados, envíe el token del dispositivo en el encabezado `x-device-token`.

Para consultar el estado de una transacción, use `GET /api/v1/otp/magic-link/transactions/{transaction_id}`. Para cancelar una transacción activa, use `DELETE` con el mismo identificador.

Los estados posibles de una transacción son `CREATED`, `SENT`, `DELIVERED`, `CONSUMED`, `EXPIRED` y `FAILED`.

> [!WARNING]
>
> El endpoint `/api/v1/magic-link/login` responde `400` si la transacción no está en estado `CONSUMED`. Responde `404` si la transacción no existe. Consuma el enlace solo una vez por transacción.

## Factores activos de una cuenta

Para saber qué factores tiene una cuenta, use los endpoints de `/api/v1/factors/account/{accountId}`.

```bash theme={null}
curl -s "https://$TENANT_HOST/middleware/api/v1/factors/account/$ACCOUNT_ID/active" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

| Endpoint | Respuesta |
| - | - |
| `GET /api/v1/factors/account/{accountId}/active` | Solo los tipos con al menos un factor habilitado. |
| `GET /api/v1/factors/account/{accountId}` | Todos los tipos de factor de la cuenta. |
| `GET /api/v1/factors/account/{accountId}/details` | Los factores de cada tipo, con su estado. |

La respuesta de `/active` tiene esta forma:

```json theme={null}
{
  "factors": ["<tipo-de-factor>"]
}
```

Si la cuenta no existe o no es visible para el token, la API responde `404` con el mensaje `Account not found`.

## Políticas de factores

Cada tipo de factor tiene una política por negocio y canal. Use la ruta `/api/v1/policies/<tipo>`.

| Tipo | Estado en FREE |
| - | - |
| `password` | Incluido |
| `email` | Incluido |
| `totp` | Incluido |
| `magic-link` | Incluido |
| `sms` | Plan pago |

Todas las operaciones de políticas usan el mismo patrón:

| Operación | Ruta | Permiso |
| - | - | - |
| Leer la política | `GET /api/v1/policies/<tipo>?businessId=...&channelId=...` | `sys_factor_policies:read` |
| Guardar la política | `POST /api/v1/policies/<tipo>` | `sys_factor_policies:create` |
| Eliminar la política | `DELETE /api/v1/policies/<tipo>` con cuerpo JSON | `sys_factor_policies:delete` |
| Listar todas | `GET /api/v1/policies/<tipo>/all` | `sys_factor_policies:read` |

```bash theme={null}
curl -s "https://$TENANT_HOST/middleware/api/v1/policies/totp?businessId=$BUSINESS_ID&channelId=$CHANNEL_ID" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

Para guardar o eliminar una política, el cuerpo requiere `businessId` y `channelId`. Al leer una política, `businessId` y `channelId` son parámetros opcionales.

### Valores predeterminados de la política de contraseña

| Campo | Predeterminado | Rango permitido |
| - | - | - |
| Longitud mínima | 12 | De 1 a 64 |
| Longitud máxima | 64 | Hasta 128 |
| Historial | Desactivado (6 contraseñas al activarlo) | De 0 a 12 |
| Intentos fallidos máximos | 5 | De 0 a 10 |
| Bloqueo automático | 30 minutos | De 30 a 120 minutos |
| Vigencia de la contraseña | Desactivada (60 días al activarla) | De 1 a 60 días |

## Eventos de factores

Los eventos registran la actividad de los factores. Para leerlos, el token necesita el permiso `sys_audit_factor:read`.

Un cliente con `role_admin` puede leer los eventos. Si prefiere un cliente más limitado, registre el scope `sys_audit_factor:read` en ese cliente.

```bash theme={null}
curl -s "https://$TENANT_HOST/middleware/api/v1/factors/events?page=1&pageSize=10" \
  -H "Authorization: Bearer $TOKEN" | jq .
```

Los endpoints de eventos son:

| Endpoint | Uso |
| - | - |
| `GET /api/v1/factors/events` | Eventos de todas las cuentas. Acepta `page`, `pageSize` y `filter`. |
| `GET /api/v1/factors/events/account/{accountId}` | Eventos de una cuenta. |
| `GET /api/v1/factors/events/operations` | Eventos con filtros por negocio, canal, cuenta, tipo de evento, fecha y orden. |
| `GET /api/v1/factors/events/operations/filter-options` | Opciones de filtro para un negocio. |

## Límites de envío

Los envíos de OTP por correo tienen un límite por cuenta. El límite predeterminado es de 5 envíos por hora en una ventana móvil. Si la cuenta supera el límite, la API responde `429` con el código `505` y `codeKey` `EMAIL.RATE_LIMIT`.

En esta vista previa, el correo de VU ONE FREE no tiene un límite por tenant. El límite por cuenta sí aplica.

## Qué sigue

> [card] [Errores de la API de VU ONE FREE](https://docs.vu-one.com/vu-one/desarrolladores/errores.md)
>
> Consulte los códigos de error de factores, el 402 y el 429.

> [card] [Cuentas, identidades y contraseñas](https://docs.vu-one.com/vu-one/desarrolladores/cuentas.md)
>
> Cree las identidades y cuentas que usan estos factores.

> [card] [Autenticación con OAuth 2.0 y OIDC](https://docs.vu-one.com/vu-one/desarrolladores/autenticacion.md)
>
> Obtenga los tokens para llamar a la API.

> [card] [Aprovisionamiento con SCIM 2.0](https://docs.vu-one.com/vu-one/desarrolladores/scim.md)
>
> Cree usuarios desde su directorio.


## Related topics

- [Errores de la API de VU ONE FREE](https://docs.vu-one.com/vu-one/desarrolladores/errores.md)
- [Cuentas, identidades y contraseñas](https://docs.vu-one.com/vu-one/desarrolladores/cuentas.md)
- [Autenticación con OAuth 2.0 y OIDC](https://docs.vu-one.com/vu-one/desarrolladores/autenticacion.md)
- [Configurar factores y políticas de autenticación](https://docs.vu-one.com/vu-one/admin/factores.md)
- [Guía para desarrolladores de VU ONE FREE](https://docs.vu-one.com/vu-one/desarrolladores/introduccion.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.