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

# 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 fue traducida automáticamente. Si encuentra errores o tiene sugerencias, [contáctenos](mailto:support@rhombus.com).
</Note>

## 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](/es/implementations/iot-sensors). El "conteo de personas" de esta página es analítica de *cámara*.
</Note>

<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.
</Warning>

## 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, en Configuración > 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.
</Note>

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.

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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"}'
  ```
</CodeGroup>

<ParamField body="name" type="string" required>
  Nombre visible de la persona.
</ParamField>

La respuesta devuelve un objeto `person`:

<ResponseField name="uuid" type="string">Identificador único de la persona.</ResponseField>
<ResponseField name="name" type="string">Nombre visible.</ResponseField>
<ResponseField name="email" type="string">Correo electrónico opcional asociado a la persona.</ResponseField>
<ResponseField name="orgUuid" type="string">Organización a la que pertenece la persona.</ResponseField>
<ResponseField name="createdOn" type="string">Cuándo se creó el registro de la persona.</ResponseField>
<ResponseField name="updatedOn" type="string">Cuándo se actualizó por última vez el registro de la persona.</ResponseField>

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

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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"
  ```
</CodeGroup>

<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.
</Warning>

La respuesta devuelve:

<ResponseField name="transactionId" type="string">El ID de transacción de esta subida (devuelto tal cual, o generado si lo omitiste). Úsalo para consultar el estado de procesamiento.</ResponseField>
<ResponseField name="fileUploadResults" type="array">Resultados de subida por archivo, cada uno con `fileName`, `success` (booleano) y `message`.</ResponseField>

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

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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"}'
  ```
</CodeGroup>

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.
</Tip>

### Etiquetar a una persona

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

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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"}'
  ```
</CodeGroup>

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

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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 '{}'
  ```
</CodeGroup>

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`                | `{ "personUuid": "..." }`                                       | Obtener una persona                         |
| `/api/faceRecognition/person/updatePerson`             | `{ "personSelectiveUpdate": { "uuid": "...", "name": "..." } }` | Actualizar nombre/correo (selectivo)        |
| `/api/faceRecognition/person/deletePerson`             | `{ "personUuid": "..." }`                                       | Eliminar una persona                        |
| `/api/faceRecognition/matchmaker/getFaceMatchmaker`    | `{ "faceId": "..." }`                                           | Obtener un rostro registrado                |
| `/api/faceRecognition/matchmaker/deleteFaceMatchmaker` | `{ "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).
</Note>

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

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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 }
    }'
  ```
</CodeGroup>

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

<ParamField body="personUuids" type="string[]">Filtra por un conjunto de personas.</ParamField>
<ParamField body="faceNames" type="string[]">Filtra por nombres exactos de personas.</ParamField>
<ParamField body="faceNameContains" type="string">Filtra por una subcadena del nombre (mínimo 3 caracteres tras recortar). Tiene precedencia sobre `faceNames` si ambos están definidos.</ParamField>
<ParamField body="labels" type="string[]">Filtra por etiquetas de persona.</ParamField>
<ParamField body="deviceUuids" type="string[]">Filtra por un conjunto de cámaras.</ParamField>
<ParamField body="locationUuids" type="string[]">Filtra por un conjunto de ubicaciones.</ParamField>
<ParamField body="hasName" type="boolean">Filtra por la presencia (`true`) o ausencia (`false`) de un nombre de persona coincidente.</ParamField>
<ParamField body="hasEmbedding" type="boolean">Filtra por la presencia o ausencia de una firma facial.</ParamField>
<ParamField body="timestampFilter" type="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.</ParamField>

### Solicitud de página

<ParamField body="maxPageSize" type="integer">Número máximo de eventos a devolver en una página.</ParamField>
<ParamField body="lastEvaluatedKey" type="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.</ParamField>

### Interpretar un evento facial

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

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

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

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

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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}'
  ```
</CodeGroup>

<ParamField body="faceMatchConfidenceThreshold" type="number">Confianza mínima de coincidencia (0–1) requerida para considerar que un rostro detectado es una coincidencia.</ParamField>

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

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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"
    }'
  ```
</CodeGroup>

<ParamField body="eventUuid" type="string" required>El evento facial a actualizar.</ParamField>
<ParamField body="personUuid" type="string">UUID de la persona correcta a asociar con el evento.</ParamField>
<ParamField body="personName" type="string">Nombre a asociar con el evento. Se ignora si `personUuid` coincide con una de las mejores coincidencias de persona del evento.</ParamField>

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"]`.

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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"
    }'
  ```
</CodeGroup>

<ParamField body="startTimeMs" type="integer" required>Inicio del rango en milisegundos epoch.</ParamField>
<ParamField body="endTimeMs" type="integer" required>Fin del rango en milisegundos epoch.</ParamField>
<ParamField body="interval" type="string" required>Tamaño del bucket. Uno de `MINUTELY`, `QUARTERHOURLY`, `HOURLY`, `DAILY`, `WEEKLY`, `MONTHLY`.</ParamField>
<ParamField body="scope" type="string" required>Alcance de agregación. Uno de `REGION`, `DEVICE`, `LOCATION`, `ORG`.</ParamField>
<ParamField body="types" type="string[]" required>Tipos de reporte a incluir. Usa `["PEOPLE"]` para el conteo de personas.</ParamField>
<ParamField body="uuid" type="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`.</ParamField>
<ParamField body="timeZone" type="string">Zona horaria IANA usada para agrupar los datos (por ejemplo, `America/Los_Angeles`).</ParamField>

Cada entrada en `timeSeriesDataPoints` incluye:

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

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

### Conteos de personas más recientes

`getMostRecentPeopleCountEvents` devuelve los últimos eventos de conteo de personas sin procesar para una cámara — útil para un mosaico de aforo en vivo.

<CodeGroup>
  ```python Python theme={null}
  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/report/getMostRecentPeopleCountEvents",
      headers=headers,
      json={"deviceUuid": "YOUR_CAMERA_UUID", "numMostRecent": 10}
  )

  for event in response.json().get("events", []):
      print(f"{event.get('eventTimestamp')}: {event.get('peopleCount')} people")
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api2.rhombussystems.com/api/report/getMostRecentPeopleCountEvents', {
    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', numMostRecent: 10 })
  });

  const data = await response.json();
  for (const event of data.events || []) {
    console.log(`${event.eventTimestamp}: ${event.peopleCount} people`);
  }
  ```

  ```bash cURL theme={null}
  curl -X POST https://api2.rhombussystems.com/api/report/getMostRecentPeopleCountEvents \
    -H "Content-Type: application/json" \
    -H "x-auth-scheme: api-token" \
    -H "x-auth-apikey: YOUR_API_KEY" \
    -d '{"deviceUuid": "YOUR_CAMERA_UUID", "numMostRecent": 10}'
  ```
</CodeGroup>

<ParamField body="deviceUuid" type="string" required>Cámara de la que leer los conteos de personas.</ParamField>
<ParamField body="numMostRecent" type="integer" required>Número de eventos de conteo más recientes a devolver.</ParamField>

Cada evento incluye `eventTimestamp` (ms epoch), `peopleCount`, `deviceUuid`, `locationUuid` y `uuid`.

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

<CodeGroup>
  ```python Python theme={null}
  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 theme={null}
  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 theme={null}
  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"
    }'
  ```
</CodeGroup>

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

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](/es/implementations/iot-sensors) — ese es un producto de hardware aparte con su propia API.
</Note>

## 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 en vivo** — muestra `getMostRecentPeopleCountEvents` para una pantalla de aforo casi en tiempo real.

## Solución de problemas

<AccordionGroup>
  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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.
  </Accordion>

  <Accordion title="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`.
  </Accordion>

  <Accordion title="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`.
  </Accordion>
</AccordionGroup>

## Próximos pasos

<CardGroup cols={2}>
  <Card title="Reportes y analíticas" icon="chart-line" href="/es/implementations/reports-analytics">
    Modelo completo de reportes de conteo, conteos de cruce de umbral y exportación CSV
  </Card>

  <Card title="LPR y vehículos" icon="car" href="/es/implementations/lpr-vehicle">
    Detecta y busca placas de matrícula y vehículos en tus cámaras
  </Card>

  <Card title="Sensores IoT" icon="temperature-half" href="/es/implementations/iot-sensors">
    Sensores físicos de ocupación, clima y ambientales
  </Card>

  <Card title="Referencia de la API" icon="code" href="/api-reference/overview">
    Explora y prueba cada endpoint de la API de Rhombus
  </Card>
</CardGroup>
