> ## 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.

# Face Match (Documento + Selfie)

> Verificar que una selfie y la foto del documento corresponden a la misma persona en una sola llamada a la API. Consulta el esquema del request, códigos.

## Resumen

Face Match es un **servicio de verificación de Gu1** que compara dos imágenes: una **selfie** (foto actual de la persona) y una **imagen de referencia del documento** (por ejemplo el retrato del DNI). La API devuelve si las caras coinciden (misma persona) y una puntuación de similitud. No hay sesión alojada ni detección de vida en tiempo real: enviás las imágenes y obtenés el resultado en una sola llamada.

<Note>
  **Cuándo usar cada uno**

  * **KYC por sesión** (`POST /api/kyc/validations`): Flujo completo con URL alojada, selfie en vivo, liveness y captura de documento. Ideal para onboarding y flujos regulados.
  * **Face Match** (`POST /api/kyc/face-match`): Enviás dos imágenes (como archivos o base64) y recibís coincidencia + puntuación. Ideal cuando ya tenés documento y selfie (por ejemplo desde tu propia app).
</Note>

## Prerrequisitos

Antes de usar Face Match:

1. **Integración Face Match activada**: Tu organización debe tener la integración KYC Face Match activada (por ejemplo en Marketplace / Integraciones).
2. **Credenciales configuradas**: La API key y el webhook secret de la integración Face Match deben estar configurados en la configuración KYC de la organización (lo define el administrador de Gu1).
3. **Umbral (opcional)**: El puntaje mínimo para aprobar (0–100) se configura en **Configuración de la organización → KYC** (tarjeta Face Match). Si no se define, el valor por defecto es 30. Solo los administradores de la organización pueden cambiar este valor.

<Info>
  Face Match es un servicio de Gu1. Toda la verificación se realiza en la infraestructura de Gu1.
</Info>

## Solicitud

### Endpoint

```
POST https://api.gu1.ai/api/kyc/face-match
```

### Content-Type

**Solo se acepta `multipart/form-data`**. Podés enviar las imágenes de dos formas (o combinar):

1. **Como archivos**: adjuntar archivos de imagen en los campos del form `documentImage` y `selfieImage`.
2. **Como cadenas base64**: enviar los mismos nombres de campo con la imagen en base64 (con o sin prefijo `data:image/...;base64,`).

Si falta una imagen **obligatoria** (ni archivo ni base64 para ese campo), la API devuelve **400**. Para cada imagen, si enviás archivo y base64, se usa el archivo.

### Headers

```
Authorization: Bearer TU_API_KEY
Content-Type: multipart/form-data; boundary=----...
```

No definas `Content-Type` a mano al usar FormData; el cliente lo setea con el boundary correcto.

Si tu cuenta usa organización, incluí:

```
X-Organization-Id: TU_ORGANIZATION_ID
```

### Campos del form

<ParamField body="documentImage" type="file | string" required>
  Imagen de referencia (por ejemplo el retrato del documento). Enviar como **archivo** (recomendado) o como **string** (base64, con o sin prefijo `data:image/...;base64,`). Formatos: JPEG, PNG, WebP, TIFF. Máx. 5MB.
</ParamField>

<ParamField body="selfieImage" type="file | string" required>
  Imagen de la selfie. Igual que `documentImage`: archivo o base64. Obligatorio.
</ParamField>

<ParamField body="entityId" type="string">
  UUID opcional de la entidad persona a asociar con esta verificación. Se usa para auditoría y para listar verificaciones por entidad.
</ParamField>

<ParamField body="scoreThreshold" type="number">
  Opcional. Umbral 0–100; resultados por debajo se rechazan. Si se omite, se usa el umbral configurado en la organización (o 30 por defecto).
</ParamField>

<ParamField body="vendorData" type="string">
  Referencia opcional (por ejemplo tu ID de usuario) para trazabilidad. Se guarda en el registro de auditoría.
</ParamField>

<ParamField query="doubleCheckRenaper" type="boolean">
  Con `true`, tras aprobar el face match de Gu1 ejecuta RENAPER **biométrico** (`validate-dni` con la selfie) y **datos** (`renaper/data`: DNI + trámite). Requiere credenciales RENAPER de la org **y** integraciones Marketplace activas: `ar_renaper_data_enrichment` y `ar_renaper_validate_dni_enrichment`. El query prevalece sobre el body.
</ParamField>

<ParamField body="doubleCheckRenaper" type="boolean">
  Igual que el query param `doubleCheckRenaper`.
</ParamField>

<ParamField body="documentNumber" type="string">
  DNI para RENAPER cuando `doubleCheckRenaper=true`. Opcional si `entityId` resuelve `taxId` o `person.idNumber`.
</ParamField>

<ParamField body="gender" type="string">
  Género para RENAPER (`M`, `F`, `male`, `female`). Obligatorio salvo resolución desde entidad vinculada.
</ParamField>

<ParamField body="personalNumber" type="string" required>
  Número de trámite para el chequeo RENAPER. **Obligatorio** con `doubleCheckRenaper=true`.
</ParamField>

El **umbral mínimo (threshold)** puede enviarse en el form; si no se envía, se lee de la configuración KYC de la organización. La respuesta incluye el umbral que se usó en la llamada.

## Respuesta

### Respuesta exitosa (200 OK)

| Campo                   | Tipo                 | Descripción                                                                                                                                                                                                         |
| ----------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `match`                 | boolean              | Si las caras se consideraron coincidentes (puntuación ≥ umbral).                                                                                                                                                    |
| `score`                 | number               | Puntuación de similitud 0–100.                                                                                                                                                                                      |
| `status`                | string               | `"approved"` \| `"declined"` \| `"in_review"`.                                                                                                                                                                      |
| `verificationId`        | string               | ID del registro de auditoría en `face_match_verifications` (soporte y listado).                                                                                                                                     |
| `scoreDeclineThreshold` | number               | Umbral (0–100) usado en esta verificación (configuración de la org).                                                                                                                                                |
| `requestId`             | string (opcional)    | ID interno de la solicitud (soporte).                                                                                                                                                                               |
| `warnings`              | string\[] (opcional) | Array de **códigos de riesgo** de la verificación (p. ej. `LOW_FACE_MATCH_SIMILARITY`, `NO_REFERENCE_IMAGE`). Ver [Códigos de riesgo en advertencias](/es/use-cases/kyc/warning-risk-codes) para todos los valores. |
| `doubleCheckRenaper`    | boolean (opcional)   | `true` si se solicitó doble chequeo RENAPER.                                                                                                                                                                        |
| `responseDoubleChecks`  | object (opcional)    | Con `doubleCheckRenaper=true`: `renaper` con datos/trámite y `renaper.renaperBiometric` (ABIS). Misma forma que [KYC validations RENAPER](/es/use-cases/kyc/create-validation#doble-chequeo-renaper-argentina).     |

Si falla RENAPER tras aprobación del proveedor, `match` pasa a `false`, `status` a `"declined"` y se añaden códigos RENAPER a `warnings`. Usá timeout HTTP ≥ 60 s con double-check.

**Ejemplo:**

```json theme={null}
{
  "match": true,
  "score": 85.5,
  "status": "approved",
  "verificationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "scoreDeclineThreshold": 30,
  "requestId": "req_xyz",
  "warnings": []
}
```

## Ejemplo de solicitud

<CodeGroup>
  ```javascript Node.js (archivos) theme={null}
  const form = new FormData();
  form.append('documentImage', documentFile); // File object
  form.append('selfieImage', selfieFile);
  form.append('entityId', '123e4567-e89b-12d3-a456-426614174000');

  const response = await fetch('https://api.gu1.ai/api/kyc/face-match', {
    method: 'POST',
    headers: { 'Authorization': 'Bearer TU_API_KEY' },
    body: form,
  });
  const result = await response.json();
  console.log('Match:', result.match, 'Score:', result.score);
  ```

  ```javascript Node.js (base64 en campos) theme={null}
  const form = new FormData();
  form.append('documentImage', 'data:image/jpeg;base64,/9j/4AAQ...');
  form.append('selfieImage', 'data:image/jpeg;base64,/9j/4AAQ...');
  form.append('entityId', '123e4567-e89b-12d3-a456-426614174000');

  const response = await fetch('https://api.gu1.ai/api/kyc/face-match', {
    method: 'POST',
    headers: { 'Authorization': 'Bearer TU_API_KEY' },
    body: form,
  });
  const result = await response.json();
  ```

  ```python Python (archivos) theme={null}
  import requests

  with open('documento.jpg', 'rb') as doc, open('selfie.jpg', 'rb') as selfie:
      files = {
          'documentImage': ('documento.jpg', doc, 'image/jpeg'),
          'selfieImage': ('selfie.jpg', selfie, 'image/jpeg'),
      }
      data = {'entityId': '123e4567-e89b-12d3-a456-426614174000'}
      response = requests.post(
          'https://api.gu1.ai/api/kyc/face-match',
          headers={'Authorization': 'Bearer TU_API_KEY'},
          files=files,
          data=data,
      )
  result = response.json()
  print('Match:', result['match'], 'Score:', result['score'])
  ```

  ```python Python (base64 en campos) theme={null}
  import requests
  import base64

  with open('documento.jpg', 'rb') as f:
      doc_b64 = 'data:image/jpeg;base64,' + base64.b64encode(f.read()).decode()
  with open('selfie.jpg', 'rb') as f:
      selfie_b64 = 'data:image/jpeg;base64,' + base64.b64encode(f.read()).decode()

  response = requests.post(
      'https://api.gu1.ai/api/kyc/face-match',
      headers={'Authorization': 'Bearer TU_API_KEY'},
      data={
          'documentImage': doc_b64,
          'selfieImage': selfie_b64,
          'entityId': '123e4567-e89b-12d3-a456-426614174000',
      },
  )
  result = response.json()
  ```

  ```curl cURL (archivos) theme={null}
  curl -X POST https://api.gu1.ai/api/kyc/face-match \
    -H "Authorization: Bearer TU_API_KEY" \
    -F "documentImage=@documento.jpg" \
    -F "selfieImage=@selfie.jpg" \
    -F "entityId=123e4567-e89b-12d3-a456-426614174000"
  ```

  ```curl cURL (base64) theme={null}
  curl -X POST https://api.gu1.ai/api/kyc/face-match \
    -H "Authorization: Bearer TU_API_KEY" \
    -F "documentImage=data:image/jpeg;base64,/9j/4AAQ..." \
    -F "selfieImage=data:image/jpeg;base64,/9j/4AAQ..." \
    -F "entityId=123e4567-e89b-12d3-a456-426614174000"
  ```
</CodeGroup>

## Respuestas de error

Códigos de error habituales:

| Código                | HTTP | Significado                                                                                                                                   |
| --------------------- | ---- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `NOT_CONFIGURED`      | 403  | Credenciales Face Match no configuradas en la organización.                                                                                   |
| `NOT_ENABLED`         | 403  | Integración Face Match no activada para esta organización.                                                                                    |
| `INVALID_REQUEST`     | 400  | Content-Type no es multipart/form-data, o faltan/imágenes inválidas (p. ej. sin archivo ni base64 para un campo obligatorio, formato/tamaño). |
| `UNAUTHORIZED`        | 401  | Credenciales de API/verificación inválidas o faltantes.                                                                                       |
| `QUOTA_EXCEEDED`      | 403  | No se pudo completar la verificación (p. ej. cuota/créditos).                                                                                 |
| `SERVICE_UNAVAILABLE` | 503  | Servicio de verificación temporalmente no disponible.                                                                                         |
| `VERIFICATION_FAILED` | 500  | No se pudo completar la verificación; reintentar u otras imágenes.                                                                            |

## Auditoría y listado de verificaciones

Cada solicitud de Face Match (éxito o fallo) se guarda en **`face_match_verifications`** para auditoría y cumplimiento. La respuesta incluye **`verificationId`** (ID del registro de auditoría). Cada registro guardado también almacena el **umbral** usado en el momento de la llamada (para mostrarlo en el historial aunque cambie el umbral de la org).

### Listar registros de auditoría

```
GET https://api.gu1.ai/api/kyc/face-match/verifications?entityId={entityId}&limit=50&offset=0
```

**Parámetros de query:**

| Parámetro  | Tipo          | Descripción                                                                   |
| ---------- | ------------- | ----------------------------------------------------------------------------- |
| `entityId` | string (UUID) | Opcional. Filtrar por entidad persona.                                        |
| `status`   | string        | Opcional. Filtrar por estado (`approved`, `declined`, `in_review`, `failed`). |
| `limit`    | number        | Máximo de ítems (por defecto 50, máx. 100).                                   |
| `offset`   | number        | Desplazamiento de paginación.                                                 |

**Respuesta:** `{ "verifications": [ ... ], "scoreDeclineThreshold": 30, "pagination": { "limit", "offset", "total" } }`. Cada verificación incluye `id`, `entityId`, `status`, `match`, `score`, `threshold` (valor usado en esa ejecución), `createdAt` y rutas de imagen opcionales para auditoría.

### Obtener verificación por ID

Obtiene un registro de auditoría de Face Match por su `verificationId` (devuelto en la respuesta del POST o en el listado).

```
GET https://api.gu1.ai/api/kyc/face-match/verifications/:id
```

**Parámetro de path:** `id` — UUID de la verificación (igual que `verificationId` del POST o del listado).

**Respuesta (200):** Un objeto de verificación con los mismos campos que cada ítem del listado: `id`, `organizationId`, `entityId`, `status`, `match`, `score`, `requestId`, `vendorData`, `errorMessage`, `triggeredByUserId`, `createdAt`, `documentStoragePath`, `selfieStoragePath`, `storageProvider`, `threshold`.

**Errores:** `404 NOT_FOUND` si el ID no existe o pertenece a otra organización; `401 UNAUTHORIZED` si no estás autenticado.

## Convivencia con KYC por sesión

* **Flujo por sesión** crea un registro en `kyc_validations`, envía al usuario a una URL alojada y actualiza el estado vía webhooks. La decisión almacenada incluye documento, liveness y face match de la sesión en vivo.
* **Face Match** crea un registro de auditoría en `face_match_verifications` y devuelve el resultado en la misma solicitud. **No** crea un registro en `kyc_validations`. Usalo cuando necesitás una verificación puntual (p. ej. «¿estas dos imágenes son la misma persona?») sin sesión alojada.
* Ambos usan la misma configuración KYC de la organización. El umbral de Face Match se define en Configuración de la organización → KYC (tarjeta Face Match).

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Crear validación KYC" icon="play" href="/es/use-cases/kyc/create-validation">
    Verificación por sesión con URL alojada
  </Card>

  <Card title="Listar validaciones" icon="list" href="/es/use-cases/kyc/list-validations">
    Listar validaciones KYC por entidad
  </Card>
</CardGroup>
