> ## Índice de la documentación
> Consulta el índice completo de la documentación en: https://developer.rhombus.com/llms.txt
> Úsalo para descubrir todas las páginas disponibles antes de seguir explorando.

# Reconocimiento facial y conteo de personas

> Registra y administra personas conocidas, consulta eventos de coincidencia facial, ajusta el umbral de coincidencia y obtén analíticas de conteo de personas de las cámaras a través de la API de Rhombus.

> **Note**: Esta página se tradujo automáticamente. Si encuentras errores o tienes sugerencias, [escríbenos](mailto:support@rhombus.com).

## Descripción general

Esta guía cubre dos capacidades de analítica de cámara distintas que es fácil confundir:

- **Reconocimiento facial (identidad)** — registra personas conocidas a partir de imágenes de rostros y luego recibe **eventos de coincidencia facial** cuando una cámara detecta un rostro que coincide. Cada evento incluye una puntuación de confianza y la persona que mejor coincide. Úsalo para reconocer _quién_ aparece en la cámara.
- **Conteo de personas (aforo)** — analítica de cámara agregada y no identificante que cuenta _cuántas_ personas cruzan el campo de visión de una cámara a lo largo del tiempo. Proviene del servicio de reportes como series temporales de conteo y conteos de ocupación. No implica identidad alguna.

A través de la API puedes:

- **Registrar y administrar personas** creando registros de persona y subiendo imágenes de rostros
- **Consultar eventos de coincidencia facial** con filtros enriquecidos y paginación
- **Ajustar el umbral de coincidencia** y corregir eventos mal identificados
- **Obtener analíticas de conteo de personas** como series temporales o los conteos más recientes por cámara

> **Note**: **No es lo mismo que los sensores de ocupación.** Rhombus también ofrece sensores de ocupación físicos (hardware Bluetooth/de movimiento) que reportan la presencia en una sala. Esos son un producto aparte servido por una API diferente — consulta la guía de [Sensores IoT](https://developer.rhombus.com/es/implementations/iot-sensors). El "conteo de personas" de esta página es analítica de _cámara_.

> **Warning**: **Privacidad biométrica.** El reconocimiento facial procesa datos biométricos. Muchas jurisdicciones regulan la recopilación, el almacenamiento y el uso de identificadores biométricos, y algunas exigen aviso o consentimiento. Revisa las leyes de privacidad biométrica que apliquen a tus ubicaciones antes de registrar rostros y confirma las políticas de tu organización. Esta guía describe únicamente la superficie de la API — no constituye asesoría legal.

## Requisitos previos

> **Note**: Antes de comenzar, asegúrate de tener:
>
> - Una **clave de API de Rhombus**. Registrar o administrar rostros requiere **permisos de administración de rostros**; leer los reportes de conteo de personas requiere acceso de lectura de reportes. Genera y limita el alcance de las claves en la [Consola de Rhombus](https://console.rhombussystems.com/settings/integrations/api-tokens/), en **Configuración → Integraciones y recursos para desarrolladores → Tokens de API**.
> - Al menos una **cámara** con la analítica correspondiente habilitada (reconocimiento facial para los flujos de identidad, conteo de personas para las analíticas de aforo).
> - **El reconocimiento facial es una función licenciada y restringida.** Si los endpoints de rostros no están disponibles para tu organización, contacta a tu equipo de cuenta de Rhombus para confirmar la habilitación.

Todas las solicitudes son `POST` a `https://api2.rhombussystems.com` y se autentican con los encabezados `x-auth-scheme` y `x-auth-apikey`. Las marcas de tiempo están en milisegundos epoch salvo que se indique lo contrario, y la confianza de coincidencia es un decimal entre `0` y `1`.

## Registrar y administrar personas conocidas

Una **persona** es un registro de identidad con nombre para el que registras rostros. Un **matchmaker** es una plantilla de rostro registrada asociada a una persona; el motor de reconocimiento compara los rostros detectados con tus matchmakers.

### Crear una persona

Crea un registro de persona con nombre para registrar rostros contra él.

```python Python
import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/person/createPerson",
    headers=headers,
    json={"name": "Jordan Rivera"}
)

person = response.json().get("person", {})
print(f"Created person {person.get('name')} -> {person.get('uuid')}")
```

```javascript JavaScript
const response = await fetch('https://api2.rhombussystems.com/api/faceRecognition/person/createPerson', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({ name: 'Jordan Rivera' })
});

const { person } = await response.json();
console.log(`Created person ${person.name} -> ${person.uuid}`);
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/faceRecognition/person/createPerson \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{"name": "Jordan Rivera"}'
```

- **`name` (body, string, required)**: Nombre visible de la persona.

La respuesta devuelve un objeto `person`:

- **`uuid` (string)**: Identificador único de la persona.
- **`name` (string)**: Nombre visible.
- **`email` (string)**: Correo electrónico opcional asociado a la persona.
- **`orgUuid` (string)**: Organización a la que pertenece la persona.
- **`createdOn` (string)**: Cuándo se creó el registro de la persona.
- **`updatedOn` (string)**: Cuándo se actualizó por última vez el registro de la persona.

### Subir una imagen de rostro

Registra un rostro subiendo una imagen `.jpg` o `.png` (máximo **5 MB**) como `multipart/form-data`. El archivo debe enviarse bajo el nombre de campo de formulario `file`. Dos parámetros de consulta opcionales controlan la transacción:

- `transaction` — un ID de transacción que proporcionas para rastrear y luego consultar la subida. Si se omite, el servidor genera uno y lo devuelve.
- `createPersonIfNotFound` — establécelo en `true` para que el sistema cree una persona nueva automáticamente cuando el rostro subido no coincide con uno existente.

```python Python
import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY"
    # NO establezcas Content-Type manualmente; requests define el límite multipart
}

with open("jordan_rivera.jpg", "rb") as f:
    files = {"file": ("jordan_rivera.jpg", f, "image/jpeg")}
    response = requests.post(
        "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/uploadFaceMatchmakers",
        headers=headers,
        params={"transaction": "enroll-jordan-001", "createPersonIfNotFound": "true"},
        files=files
    )

data = response.json()
print(f"Transaction: {data.get('transactionId')}")
for result in data.get("fileUploadResults", []):
    print(f"  {result.get('fileName')}: success={result.get('success')} {result.get('message', '')}")
```

```javascript JavaScript
import fs from 'fs';

const form = new FormData();
const buffer = fs.readFileSync('jordan_rivera.jpg');
form.append('file', new Blob([buffer], { type: 'image/jpeg' }), 'jordan_rivera.jpg');

const url = new URL('https://api2.rhombussystems.com/api/faceRecognition/matchmaker/uploadFaceMatchmakers');
url.searchParams.set('transaction', 'enroll-jordan-001');
url.searchParams.set('createPersonIfNotFound', 'true');

const response = await fetch(url, {
  method: 'POST',
  headers: {
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: form
});

const data = await response.json();
console.log(`Transaction: ${data.transactionId}`);
for (const result of data.fileUploadResults || []) {
  console.log(`  ${result.fileName}: success=${result.success} ${result.message || ''}`);
}
```

```bash cURL
curl -X POST "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/uploadFaceMatchmakers?transaction=enroll-jordan-001&createPersonIfNotFound=true" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -F "file=@jordan_rivera.jpg;type=image/jpeg"
```

> **Warning**: Sube una imagen por solicitud. Envía exactamente una parte `file`, mantenla por debajo de 5 MB y usa `.jpg` o `.png`. Deja que tu cliente HTTP defina el límite de `Content-Type: multipart/form-data` — no lo establezcas manualmente.

La respuesta devuelve:

- **`transactionId` (string)**: El ID de transacción de esta subida (devuelto tal cual, o generado si lo omitiste). Úsalo para consultar el estado de procesamiento.
- **`fileUploadResults` (array)**: Resultados de subida por archivo, cada uno con `fileName`, `success` (booleano) y `message`.

### Consultar la transacción de subida

Las imágenes de rostros se procesan de forma asíncrona. Consulta la transacción para conocer el `faceId` resultante y el `personUuid` al que se registró o vinculó el rostro.

```python Python
import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

for _ in range(10):
    response = requests.post(
        "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/findFaceUploadMetadataByTransaction",
        headers=headers,
        json={"transactionId": "enroll-jordan-001"}
    )
    metadata = response.json().get("faceUploadMetadata", [])
    if metadata and all(m.get("success") is not None for m in metadata):
        for m in metadata:
            print(f"faceId={m.get('faceId')} personUuid={m.get('personUuid')} "
                  f"success={m.get('success')} {m.get('errorMsg', '')}")
        break
    time.sleep(2)
```

```javascript JavaScript
const headers = {
  'Content-Type': 'application/json',
  'x-auth-scheme': 'api-token',
  'x-auth-apikey': 'YOUR_API_KEY'
};

for (let i = 0; i < 10; i++) {
  const response = await fetch('https://api2.rhombussystems.com/api/faceRecognition/matchmaker/findFaceUploadMetadataByTransaction', {
    method: 'POST',
    headers,
    body: JSON.stringify({ transactionId: 'enroll-jordan-001' })
  });
  const { faceUploadMetadata = [] } = await response.json();
  if (faceUploadMetadata.length && faceUploadMetadata.every(m => m.success != null)) {
    for (const m of faceUploadMetadata) {
      console.log(`faceId=${m.faceId} personUuid=${m.personUuid} success=${m.success} ${m.errorMsg || ''}`);
    }
    break;
  }
  await new Promise(r => setTimeout(r, 2000));
}
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/faceRecognition/matchmaker/findFaceUploadMetadataByTransaction \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{"transactionId": "enroll-jordan-001"}'
```

Cada entrada de `faceUploadMetadata` incluye:

| Campo             | Tipo    | Descripción                                                           |
| ----------------- | ------- | --------------------------------------------------------------------- |
| `transactionId`   | string  | La transacción a la que pertenece esta subida                         |
| `faceId`          | string  | Identificador del matchmaker de rostro registrado (una vez procesado) |
| `personUuid`      | string  | Persona a la que se registró o vinculó el rostro                      |
| `success`         | boolean | Si el procesamiento tuvo éxito                                        |
| `errorMsg`        | string  | Detalle del error cuando `success` es `false`                         |
| `createdAtMillis` | integer | Marca de tiempo de la subida en milisegundos epoch                    |
| `origS3Key`       | string  | Clave de almacenamiento de la imagen original subida                  |

> **Tip**: ¿Prefieres registrar a una persona a partir de un rostro que la cámara ya capturó? Usa `createFaceMatchmakerFromSighting` con el `faceEventUuid` de un evento facial existente y el `personUuid` de destino para convertir ese avistamiento en una plantilla registrada — sin necesidad de subir nada.

### Etiquetar a una persona

Adjunta etiquetas (por ejemplo, `employee` o `visitor`) para organizar a las personas y filtrar los eventos faciales más adelante.

```python Python
import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/person/addPersonLabel",
    headers=headers,
    json={"personUuid": "YOUR_PERSON_UUID", "label": "employee"}
)
print(response.json())
```

```javascript JavaScript
const response = await fetch('https://api2.rhombussystems.com/api/faceRecognition/person/addPersonLabel', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({ personUuid: 'YOUR_PERSON_UUID', label: 'employee' })
});
console.log(await response.json());
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/faceRecognition/person/addPersonLabel \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{"personUuid": "YOUR_PERSON_UUID", "label": "employee"}'
```

Usa `removePersonLabel` con el mismo cuerpo para quitar una etiqueta, y `findPersonLabelsByOrg` (cuerpo vacío `{}`) para obtener un mapa `labelsByPerson` con las etiquetas de cada persona.

### Listar personas y matchmakers

Lista todas las personas registradas con `findPeopleByOrg`, y lista las plantillas de rostro registradas con `findFaceMatchmakersByOrg` (o `findFaceMatchmakersByPerson` para una sola persona).

```python Python
import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

people = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/person/findPeopleByOrg",
    headers=headers, json={}
).json().get("people", [])

for person in people:
    print(f"{person.get('name')} ({person.get('uuid')})")

matchmakers = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/findFaceMatchmakersByOrg",
    headers=headers, json={}
).json().get("faceMatchmakers", [])

for mm in matchmakers:
    print(f"faceId={mm.get('id')} personUuid={mm.get('personUuid')} uploaded={mm.get('uploaded')}")
```

```javascript JavaScript
const headers = {
  'Content-Type': 'application/json',
  'x-auth-scheme': 'api-token',
  'x-auth-apikey': 'YOUR_API_KEY'
};

const people = await (await fetch('https://api2.rhombussystems.com/api/faceRecognition/person/findPeopleByOrg', {
  method: 'POST', headers, body: '{}'
})).json();
for (const person of people.people || []) {
  console.log(`${person.name} (${person.uuid})`);
}

const matchmakers = await (await fetch('https://api2.rhombussystems.com/api/faceRecognition/matchmaker/findFaceMatchmakersByOrg', {
  method: 'POST', headers, body: '{}'
})).json();
for (const mm of matchmakers.faceMatchmakers || []) {
  console.log(`faceId=${mm.id} personUuid=${mm.personUuid} uploaded=${mm.uploaded}`);
}
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/faceRecognition/person/findPeopleByOrg \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{}'

curl -X POST https://api2.rhombussystems.com/api/faceRecognition/matchmaker/findFaceMatchmakersByOrg \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{}'
```

Cada matchmaker de rostro incluye `id` (el ID del rostro), `personUuid`, `orgUuid`, `uploaded` (booleano) y `createdOn`.

### Actualizar o eliminar personas y rostros

| Endpoint                                                                                                                                                            | Cuerpo                                                          | Propósito                                   |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------- |
| [`/api/faceRecognition/person/getPerson`](https://developer.rhombus.com/api-reference/face-recognition-person-webservice/get-a-person)                              | `{ "personUuid": "..." }`                                       | Obtener una persona                         |
| [`/api/faceRecognition/person/updatePerson`](https://developer.rhombus.com/api-reference/face-recognition-person-webservice/update-person)                          | `{ "personSelectiveUpdate": { "uuid": "...", "name": "..." } }` | Actualizar nombre/correo (selectivo)        |
| [`/api/faceRecognition/person/deletePerson`](https://developer.rhombus.com/api-reference/face-recognition-person-webservice/delete-person)                          | `{ "personUuid": "..." }`                                       | Eliminar una persona                        |
| [`/api/faceRecognition/matchmaker/getFaceMatchmaker`](https://developer.rhombus.com/api-reference/face-recognition-matchmaker-webservice/get-face-matchmaker)       | `{ "faceId": "..." }`                                           | Obtener un rostro registrado                |
| [`/api/faceRecognition/matchmaker/deleteFaceMatchmaker`](https://developer.rhombus.com/api-reference/face-recognition-matchmaker-webservice/delete-face-matchmaker) | `{ "faceId": "..." }`                                           | Eliminar una plantilla de rostro registrada |

> **Note**: Para administrar la retención y eliminación de datos biométricos, usa `deletePerson` (elimina la persona y su registro) y `deleteFaceMatchmaker` (elimina un solo rostro registrado). Los eventos faciales se pueden eliminar individualmente con `deleteFaceEvent` (más abajo).

## Consultar eventos de coincidencia facial

Cuando una cámara detecta un rostro, genera un **evento facial**. Recupera eventos con `findFaceEventsByOrg`, que toma un `searchFilter` y un `pageRequest`.

```python Python
import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
one_day_ago_ms = now_ms - (24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/faceEvent/findFaceEventsByOrg",
    headers=headers,
    json={
        "searchFilter": {
            "hasName": True,
            "timestampFilter": {
                "rangeStart": str(one_day_ago_ms),
                "rangeEnd": str(now_ms)
            }
        },
        "pageRequest": {"maxPageSize": 50}
    }
)

data = response.json()
for event in data.get("faceEvents", []):
    match = event.get("selectedPersonMatch") or {}
    print(f"{event.get('faceName')} on {event.get('deviceUuid')} "
          f"confidence={match.get('confidence')} at {event.get('eventTimestamp')}")

# Obtén la página siguiente si hay lastEvaluatedKey presente
next_key = data.get("lastEvaluatedKey")
```

```javascript JavaScript
const headers = {
  'Content-Type': 'application/json',
  'x-auth-scheme': 'api-token',
  'x-auth-apikey': 'YOUR_API_KEY'
};

const now = Date.now();
const oneDayAgo = now - (24 * 60 * 60 * 1000);

const response = await fetch('https://api2.rhombussystems.com/api/faceRecognition/faceEvent/findFaceEventsByOrg', {
  method: 'POST',
  headers,
  body: JSON.stringify({
    searchFilter: {
      hasName: true,
      timestampFilter: {
        rangeStart: String(oneDayAgo),
        rangeEnd: String(now)
      }
    },
    pageRequest: { maxPageSize: 50 }
  })
});

const data = await response.json();
for (const event of data.faceEvents || []) {
  const match = event.selectedPersonMatch || {};
  console.log(`${event.faceName} on ${event.deviceUuid} confidence=${match.confidence} at ${event.eventTimestamp}`);
}
const nextKey = data.lastEvaluatedKey;
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/faceRecognition/faceEvent/findFaceEventsByOrg \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{
    "searchFilter": {
      "hasName": true,
      "timestampFilter": {
        "rangeStart": "1712620800000",
        "rangeEnd": "1712707200000"
      }
    },
    "pageRequest": { "maxPageSize": 50 }
  }'
```

### Filtro de búsqueda

Todos los campos de `searchFilter` son opcionales. Cuando se omite `timestampFilter`, la búsqueda usa por defecto los **últimos 7 días**.

- **`personUuids` (body, string\[])**: Filtra por un conjunto de personas.
- **`faceNames` (body, string\[])**: Filtra por nombres exactos de personas.
- **`faceNameContains` (body, string)**: Filtra por una subcadena del nombre (mínimo 3 caracteres tras recortar). Tiene precedencia sobre `faceNames` si ambos están definidos.
- **`labels` (body, string\[])**: Filtra por etiquetas de persona.
- **`deviceUuids` (body, string\[])**: Filtra por un conjunto de cámaras.
- **`locationUuids` (body, string\[])**: Filtra por un conjunto de ubicaciones.
- **`hasName` (body, boolean)**: Filtra por la presencia (`true`) o ausencia (`false`) de un nombre de persona coincidente.
- **`hasEmbedding` (body, boolean)**: Filtra por la presencia o ausencia de una firma facial.
- **`timestampFilter` (body, object)**: Ventana temporal con `rangeStart` y `rangeEnd`, cada uno una cadena que contiene un valor en milisegundos epoch (inclusive). Por defecto usa los últimos 7 días.

### Solicitud de página

- **`maxPageSize` (body, integer)**: Número máximo de eventos a devolver en una página.
- **`lastEvaluatedKey` (body, string)**: Cursor de paginación. Pasa el `lastEvaluatedKey` de la respuesta anterior para obtener la página siguiente. Ausente cuando no hay más resultados.

### Interpretar un evento facial

Cada evento en `faceEvents` es un objeto con estos campos:

- **`uuid` (string)**: Identificador único del evento facial.
- **`eventTimestamp` (integer)**: Cuándo se detectó el rostro, en milisegundos epoch.
- **`faceName` (string)**: Nombre de la persona coincidente. Igual al `name` de `selectedPersonMatch` cuando se seleccionó una coincidencia.
- **`personUuid` (string)**: UUID de la persona coincidente, si la hay.
- **`deviceUuid` (string)**: Cámara que generó el evento.
- **`locationUuid` (string)**: Ubicación de la cámara.
- **`detectionConfidence` (number)**: Confianza (0–1) de que la imagen detectada es un rostro.
- **`embeddingConfidence` (number)**: Confianza (0–1) asociada a la firma facial generada.
- **`hasEmbedding` (boolean)**: Si el evento tiene una firma facial.
- **`selectedPersonMatch` (object)**: La coincidencia de persona elegida, con `uuid`, `name`, `faceId` y `confidence` (0–1). Nula cuando no se seleccionó ninguna coincidencia.
- **`topPersonMatches` (array)**: Las mejores coincidencias candidatas para el rostro, cada una con `uuid`, `name`, `faceId` y `confidence` (0–1). Útil para revisar coincidencias cercanas.
- **`imageS3Key` (string)**: Clave de almacenamiento de la imagen del evento.
- **`thumbnailS3Key` (string)**: Clave de almacenamiento de la miniatura del evento.

> **Tip**: Obtén un solo evento por su UUID con `getFaceEvent` (cuerpo `{ "eventUuid": "..." }`).

## Ajustar la coincidencia y corregir eventos

### Leer y actualizar el umbral de coincidencia

El umbral de coincidencia controla qué tan estricta es la coincidencia facial. Es un valor de confianza entre `0` y `1` — un valor más alto exige una coincidencia más cercana (menos falsos positivos, más omisiones); un valor más bajo es más permisivo.

```python Python
import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

# Leer el umbral actual
current = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/getFaceMatchingConfig",
    headers=headers, json={}
).json()
print(f"Current threshold: {current.get('faceMatchConfidenceThreshold')}")

# Subir el umbral para reducir falsos positivos
updated = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/matchmaker/updateFaceMatchingConfig",
    headers=headers,
    json={"faceMatchConfidenceThreshold": 0.8}
).json()
print(f"New threshold: {updated.get('faceMatchConfidenceThreshold')}")
```

```javascript JavaScript
const headers = {
  'Content-Type': 'application/json',
  'x-auth-scheme': 'api-token',
  'x-auth-apikey': 'YOUR_API_KEY'
};

const current = await (await fetch('https://api2.rhombussystems.com/api/faceRecognition/matchmaker/getFaceMatchingConfig', {
  method: 'POST', headers, body: '{}'
})).json();
console.log(`Current threshold: ${current.faceMatchConfidenceThreshold}`);

const updated = await (await fetch('https://api2.rhombussystems.com/api/faceRecognition/matchmaker/updateFaceMatchingConfig', {
  method: 'POST', headers, body: JSON.stringify({ faceMatchConfidenceThreshold: 0.8 })
})).json();
console.log(`New threshold: ${updated.faceMatchConfidenceThreshold}`);
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/faceRecognition/matchmaker/getFaceMatchingConfig \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{}'

curl -X POST https://api2.rhombussystems.com/api/faceRecognition/matchmaker/updateFaceMatchingConfig \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{"faceMatchConfidenceThreshold": 0.8}'
```

- **`faceMatchConfidenceThreshold` (body, number)**: Confianza mínima de coincidencia (0–1) requerida para considerar que un rostro detectado es una coincidencia.

### Corregir un evento mal identificado

Reasigna un evento facial a la persona correcta (o establece un nombre directamente) con `updateFaceEvent`. Así corriges coincidencias erróneas.

```python Python
import requests

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

response = requests.post(
    "https://api2.rhombussystems.com/api/faceRecognition/faceEvent/updateFaceEvent",
    headers=headers,
    json={
        "eventUuid": "YOUR_FACE_EVENT_UUID",
        "personUuid": "CORRECT_PERSON_UUID"
    }
)
print(response.json())
```

```javascript JavaScript
const response = await fetch('https://api2.rhombussystems.com/api/faceRecognition/faceEvent/updateFaceEvent', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    eventUuid: 'YOUR_FACE_EVENT_UUID',
    personUuid: 'CORRECT_PERSON_UUID'
  })
});
console.log(await response.json());
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/faceRecognition/faceEvent/updateFaceEvent \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{
    "eventUuid": "YOUR_FACE_EVENT_UUID",
    "personUuid": "CORRECT_PERSON_UUID"
  }'
```

- **`eventUuid` (body, string, required)**: El evento facial a actualizar.
- **`personUuid` (body, string)**: UUID de la persona correcta a asociar con el evento.
- **`personName` (body, string)**: Nombre a asociar con el evento. Se ignora si `personUuid` coincide con una de las mejores coincidencias de persona del evento.

Para eliminar un evento por completo, llama a `deleteFaceEvent` con `{ "eventUuid": "..." }`.

## Analíticas de conteo de personas

El conteo de personas es analítica de cámara agregada — cuenta personas sin identificarlas, servida por el servicio de reportes. Úsalo para tráfico de personas, aforo y paneles de tendencias.

### Serie temporal de conteo

`getCountReportV2` devuelve una serie temporal de conteos agrupados por intervalo. Para el conteo de personas, establece `types` en `["PEOPLE"]`.

```python Python
import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
week_ago_ms = now_ms - (7 * 24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/report/getCountReportV2",
    headers=headers,
    json={
        "startTimeMs": week_ago_ms,
        "endTimeMs": now_ms,
        "interval": "DAILY",
        "scope": "DEVICE",
        "types": ["PEOPLE"],
        "uuid": "YOUR_CAMERA_UUID",
        "timeZone": "America/Los_Angeles"
    }
)

data = response.json()
for point in data.get("timeSeriesDataPoints", []):
    counts = point.get("eventCountMap", {})
    print(f"{point.get('dateLocal')}: {counts.get('PEOPLE', 0)} people")
```

```javascript JavaScript
const now = Date.now();
const weekAgo = now - (7 * 24 * 60 * 60 * 1000);

const response = await fetch('https://api2.rhombussystems.com/api/report/getCountReportV2', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    startTimeMs: weekAgo,
    endTimeMs: now,
    interval: 'DAILY',
    scope: 'DEVICE',
    types: ['PEOPLE'],
    uuid: 'YOUR_CAMERA_UUID',
    timeZone: 'America/Los_Angeles'
  })
});

const data = await response.json();
for (const point of data.timeSeriesDataPoints || []) {
  const counts = point.eventCountMap || {};
  console.log(`${point.dateLocal}: ${counts.PEOPLE || 0} people`);
}
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/report/getCountReportV2 \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{
    "startTimeMs": 1712016000000,
    "endTimeMs": 1712620800000,
    "interval": "DAILY",
    "scope": "DEVICE",
    "types": ["PEOPLE"],
    "uuid": "YOUR_CAMERA_UUID",
    "timeZone": "America/Los_Angeles"
  }'
```

- **`startTimeMs` (body, integer, required)**: Inicio del rango en milisegundos epoch.
- **`endTimeMs` (body, integer, required)**: Fin del rango en milisegundos epoch.
- **`interval` (body, string, required)**: Tamaño del bucket. Uno de `MINUTELY`, `QUARTERHOURLY`, `HOURLY`, `DAILY`, `WEEKLY`, `MONTHLY`.
- **`scope` (body, string, required)**: Alcance de agregación. Uno de `REGION`, `DEVICE`, `LOCATION`, `ORG`.
- **`types` (body, string\[], required)**: Tipos de reporte a incluir. Usa `["PEOPLE"]` para el conteo de personas.
- **`uuid` (body, string)**: UUID de destino para el alcance elegido (por ejemplo, un UUID de cámara cuando `scope` es `DEVICE`). Omítelo para el alcance `ORG`.
- **`timeZone` (body, string)**: Zona horaria IANA usada para agrupar los datos (por ejemplo, `America/Los_Angeles`).

Cada entrada en `timeSeriesDataPoints` incluye:

- **`dateUtc` (string)**: Inicio del bucket en UTC.
- **`dateLocal` (string)**: Inicio del bucket en la zona horaria solicitada.
- **`eventCountMap` (object)**: Mapa de tipo de reporte a conteo para el bucket (por ejemplo, `{ "PEOPLE": 42 }`).
- **`reportingDevicesMap` (object)**: Mapa que describe qué dispositivos reportaron en el bucket.

> **Note**: `getCountReportV2` admite muchos `types` además de `PEOPLE` — consulta la guía de [Reportes y analíticas](https://developer.rhombus.com/es/implementations/reports-analytics) para conocer el modelo completo de reportes, endpoints de analítica adicionales y la exportación CSV.

### Conteos de ocupación a lo largo del tiempo

`getOccupancyCountsV2` devuelve una serie temporal de conteos de ocupación derivados de la cámara para una sola cámara.

```python Python
import requests
import time

headers = {
    "x-auth-scheme": "api-token",
    "x-auth-apikey": "YOUR_API_KEY",
    "Content-Type": "application/json"
}

now_ms = int(time.time() * 1000)
day_ago_ms = now_ms - (24 * 60 * 60 * 1000)

response = requests.post(
    "https://api2.rhombussystems.com/api/report/getOccupancyCountsV2",
    headers=headers,
    json={
        "deviceUuid": "YOUR_CAMERA_UUID",
        "startTimeMs": day_ago_ms,
        "endTimeMs": now_ms,
        "interval": "HOURLY"
    }
)

for point in response.json().get("timeSeriesDataPoints", []):
    print(f"{point.get('dateLocal')}: {point.get('eventCountMap')}")
```

```javascript JavaScript
const now = Date.now();
const dayAgo = now - (24 * 60 * 60 * 1000);

const response = await fetch('https://api2.rhombussystems.com/api/report/getOccupancyCountsV2', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-auth-scheme': 'api-token',
    'x-auth-apikey': 'YOUR_API_KEY'
  },
  body: JSON.stringify({
    deviceUuid: 'YOUR_CAMERA_UUID',
    startTimeMs: dayAgo,
    endTimeMs: now,
    interval: 'HOURLY'
  })
});

const data = await response.json();
for (const point of data.timeSeriesDataPoints || []) {
  console.log(`${point.dateLocal}:`, point.eventCountMap);
}
```

```bash cURL
curl -X POST https://api2.rhombussystems.com/api/report/getOccupancyCountsV2 \
  -H "Content-Type: application/json" \
  -H "x-auth-scheme: api-token" \
  -H "x-auth-apikey: YOUR_API_KEY" \
  -d '{
    "deviceUuid": "YOUR_CAMERA_UUID",
    "startTimeMs": 1712534400000,
    "endTimeMs": 1712620800000,
    "interval": "HOURLY"
  }'
```

- **`deviceUuid` (body, string, required)**: Cámara de la que leer los conteos de ocupación.
- **`startTimeMs` (body, integer, required)**: Inicio del rango en milisegundos epoch.
- **`endTimeMs` (body, integer, required)**: Fin del rango en milisegundos epoch.
- **`interval` (body, string, required)**: Tamaño del bucket (`MINUTELY`, `QUARTERHOURLY`, `HOURLY`, `DAILY`, `WEEKLY`, `MONTHLY`).

Cada punto de datos añade `timestampMs` y `approximateTimestampMsMap` a los campos estándar `dateUtc`, `dateLocal`, `eventCountMap` y `reportingDevicesMap`.

> **Note**: Este reporte de ocupación es **derivado de la cámara**. Para la presencia en salas medida por sensores de ocupación físicos (Bluetooth/de movimiento), consulta la guía de [Sensores IoT](https://developer.rhombus.com/es/implementations/iot-sensors) — ese es un producto de hardware aparte con su propia API.

## Referencia: enums de reportes

| Enum            | Valores                                                                                                              |
| --------------- | -------------------------------------------------------------------------------------------------------------------- |
| Tipo de reporte | `CROWD`, `PEOPLE`, `FACES`, `MOTION`, `BANDWIDTH`, `VEHICLES`, `LICENSEPLATES`, `ALERTS`, `AM_VERIFICATION`, `DWELL` |
| Intervalo       | `MINUTELY`, `QUARTERHOURLY`, `HOURLY`, `DAILY`, `WEEKLY`, `MONTHLY`                                                  |
| Alcance         | `REGION`, `DEVICE`, `LOCATION`, `ORG`                                                                                |

## Casos de uso

- **Alertas de lista de vigilancia** — registra personas de interés y luego consulta `findFaceEventsByOrg` filtrando por `personUuids` o `labels` para marcar avistamientos.
- **Reconciliación de acceso** — cruza los eventos faciales con la actividad de control de acceso para confirmar que la persona que usó su credencial coincide con la que vio la cámara.
- **Ciclo de calidad del modelo** — revisa `topPersonMatches` en eventos de baja confianza, corrígelos con `updateFaceEvent` y ajusta `faceMatchConfidenceThreshold` para equilibrar falsos positivos y omisiones.
- **Paneles de tráfico de personas** — grafica `getCountReportV2` con `types: ["PEOPLE"]` para tendencias de tráfico diarias u horarias por cámara o ubicación.
- **Mosaicos de aforo** — grafica `getOccupancyCountsV2` para una cámara y muestra la ocupación a lo largo del día.

## Solución de problemas

**Los endpoints de rostros devuelven un error o no están disponibles**

El reconocimiento facial es una función licenciada y restringida. Confirma que tu organización esté habilitada y que tu clave de API tenga permisos de administración de rostros. Contacta a tu equipo de cuenta de Rhombus si los endpoints no están disponibles.

**uploadFaceMatchmakers rechaza mi archivo**

Envía exactamente un archivo por solicitud bajo el nombre de campo de formulario `file`, mantenlo por debajo de 5 MB y usa `.jpg` o `.png`. Deja que tu cliente HTTP defina el límite multipart de `Content-Type` — no establezcas `Content-Type` manualmente.

**findFaceUploadMetadataByTransaction aún no devuelve nada**

Las subidas se procesan de forma asíncrona. Consulta la transacción hasta que cada entrada reporte un valor de `success`. Cuando `success` es `false`, lee `errorMsg` para conocer el motivo.

**No regresan eventos faciales**

Si omites `timestampFilter`, la búsqueda solo cubre los últimos 7 días. Amplía la ventana con `rangeStart`/`rangeEnd` (cadenas en milisegundos epoch) y relaja filtros como `hasName` o `personUuids`. Recuerda paginar con `lastEvaluatedKey`.

**Demasiadas coincidencias falsas (o muy pocas)**

Ajusta `faceMatchConfidenceThreshold` con `updateFaceMatchingConfig`. Súbelo (más cerca de 1) para reducir falsos positivos; bájalo para capturar más coincidencias potenciales. Corrige los errores individuales con `updateFaceEvent`.

## Próximos pasos

- **[Reportes y analíticas](https://developer.rhombus.com/es/implementations/reports-analytics)**: Modelo completo de reportes de conteo, conteos de cruce de umbral y exportación CSV
- **[LPR y vehículos](https://developer.rhombus.com/es/implementations/lpr-vehicle)**: Detecta y busca placas de matrícula y vehículos en tus cámaras
- **[Sensores IoT](https://developer.rhombus.com/es/implementations/iot-sensors)**: Sensores físicos de ocupación, clima y ambientales
- **[Referencia de la API](https://developer.rhombus.com/api-reference/overview)**: Explora y prueba cada endpoint de la API de Rhombus
