# Registrar (/docs/developers/api-reference/time-clock-events/create)

## Descripcion general [#descripcion-general]

Este endpoint recibe un **lote de checadas** desde un reloj checador o una integración. Está pensado para que el dispositivo reenvíe lotes completos sin miedo: la ingesta es **idempotente**, una misma marca (mismo empleo, misma marca de tiempo) nunca se duplica.

El lote es **todo o nada**: si una sola fila es inválida o su empleado no se puede resolver, no se registra ninguna checada y la respuesta lista los problemas fila por fila.

Cuando el lote entra, la plataforma vuelve a evaluar los días que tocaste y deriva **retardos**, nunca faltas. Si la organización activó **Derivar faltas** y uno de esos días ya tenía una falta derivada por no tener checadas, la checada nueva la retira. La derivación solo corre si la organización tiene activado el interruptor **Derivar retardos** en Configuración → Reloj checador, y si el empleo tiene un horario efectivo (el de su contrato, o el horario default de la organización).

Requiere que la API key tenga el permiso **Registrar checadas**.

## Endpoint [#endpoint]

`POST https://app.integranom.com/api/v1/time-clock-events`

## Registra un lote de checadas [#registra-un-lote-de-checadas]

***

Hasta **100 checadas** por solicitud. El ejemplo sube la entrada y la salida de un empleado cuyo horario empieza a las 9:00 con 15 minutos de tolerancia: como llegó a las 9:20, se deriva un retardo → `derived.created: 1`.

`created` son las checadas nuevas; `duplicates`, las que ya existían (o venían repetidas en el mismo lote). Reenviar el lote completo devuelve `created: 0` y no vuelve a derivar nada — la derivación anterior sigue en pie.

## Campos del cuerpo [#campos-del-cuerpo]

| Campo           | Tipo      | Requerido | Notas                                                                                               |
| --------------- | --------- | --------- | --------------------------------------------------------------------------------------------------- |
| `events`        | object\[] | Sí        | 1–100 checadas.                                                                                     |
| `confirm_retro` | boolean   | No        | Autoriza que los días dentro de un periodo cerrado se registren como ajuste retroactivo. Ver abajo. |

## Campos de cada checada [#campos-de-cada-checada]

| Campo             | Tipo              | Requerido | Notas                                                                                                                |
| ----------------- | ----------------- | --------- | -------------------------------------------------------------------------------------------------------------------- |
| `employee_number` | integer           | Sí        | Número del empleado dentro de la organización.                                                                       |
| `timestamp`       | string (ISO 8601) | Sí        | Instante de la marca, **con zona horaria** (`...Z` o `...-06:00`). Una hora local sin offset se rechaza por ambigua. |
| `direction`       | `"in"` \| `"out"` | No        | Dirección declarada por el dispositivo. Una marca `"out"` nunca cuenta como la llegada del día.                      |
| `device_id`       | string            | No        | Identificador del dispositivo. Máximo 120 caracteres.                                                                |

**cURL**

```bash
curl -X POST "https://app.integranom.com/api/v1/time-clock-events" \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "events": [
    {
      "employee_number": 142,
      "timestamp": "2026-06-05T09:20:00-06:00",
      "direction": "in",
      "device_id": "RELOJ-PLANTA-1"
    },
    {
      "employee_number": 142,
      "timestamp": "2026-06-05T18:03:00-06:00",
      "direction": "out",
      "device_id": "RELOJ-PLANTA-1"
    }
  ]
}'
```

**Respuesta**

```json
{
"summary": {
  "total": 2,
  "created": 2,
  "duplicates": 0
},
"derived": {
  "created": 1,
  "updated": 0,
  "deleted": 0
}
}
```

## Dias dentro de un periodo cerrado (ajuste retroactivo) [#dias-dentro-de-un-periodo-cerrado-ajuste-retroactivo]

***

Cerrar un periodo es definitivo. Un retardo derivado de una checada con fecha dentro de sus días no reescribe la nómina ya pagada: se registra como un **ajuste retroactivo** que corrige la diferencia en el **siguiente** periodo.

Por eso, si el `local_date` de alguna checada cae en un periodo cerrado, el lote se rechaza con `409 RETRO_CONFIRMATION_REQUIRED` (con `retro_periods`, los periodos afectados) y **no se registra nada**. Reenvíalo con **`confirm_retro: true`** para aceptar.

La puerta solo aplica cuando la derivación de retardos está activada: con el interruptor apagado una checada no deriva retardos y el lote entra sin confirmación. Con **Derivar faltas** encendida, una checada de un día que tenía una falta derivada la retira aunque el periodo esté cerrado; ese cambio entra solo como ajuste retroactivo, sin pedir `confirm_retro`.

**cURL**

```bash
curl -X POST "https://app.integranom.com/api/v1/time-clock-events" \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "confirm_retro": true,
  "events": [
    {
      "employee_number": 1,
      "timestamp": "2026-01-05T09:40:00-06:00",
      "direction": "in"
    }
  ]
}'
```

**Respuesta**

```json
{
"summary": {
  "total": 1,
  "created": 1,
  "duplicates": 0
},
"derived": {
  "created": 1,
  "updated": 0,
  "deleted": 0
}
}
```

## Como se identifica al empleado [#como-se-identifica-al-empleado]

Cada checada debe traer `employee_number`: el número del empleado dentro de la organización. Con él se busca su empleo activo o pendiente, y ahí aterriza la checada.

Si el número no existe en la organización → `EMPLOYEE_NOT_FOUND`. Si existe pero la persona no tiene un empleo activo ni pendiente → `NO_ACTIVE_EMPLOYMENT`.

Cuando alguien tiene más de un empleo abierto en la misma organización, la checada aterriza en el más reciente.

## Que se deriva (y que no) [#que-se-deriva-y-que-no]

| Situación                                                           | Resultado                                                                                                                                            |
| ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Primera marca del día más tarde que la hora de entrada + tolerancia | **Retardo** en el registro de asistencia, con origen `time_clock`.                                                                                   |
| Día laborable **sin** checadas                                      | Nada al recibir el lote. Con **Derivar faltas** encendida, un proceso nocturno lo registra como falta con origen `time_clock` cuando el día termina. |
| Día con una falta derivada que recibe su primera checada            | La falta derivada se elimina.                                                                                                                        |
| Primera marca del día en o después de la hora de salida             | Nada — se asume una salida sin registrar del día anterior.                                                                                           |
| El día ya tiene una incidencia capturada a mano                     | Nada. Lo manual siempre gana; el motor no pisa lo que RH ya registró.                                                                                |
| Turno nocturno: llegada después de medianoche                       | Nada. Esa marca cae en el día natural siguiente y no se compara contra la entrada del turno.                                                         |
| El empleo no tiene horario efectivo                                 | Nada.                                                                                                                                                |

## Forma de la respuesta [#forma-de-la-respuesta]

`201` — el lote se registró.

| Campo                | Significado                                               |
| -------------------- | --------------------------------------------------------- |
| `summary.total`      | Checadas enviadas en la solicitud.                        |
| `summary.created`    | Checadas nuevas registradas.                              |
| `summary.duplicates` | Checadas que ya existían o venían repetidas en el lote.   |
| `derived.created`    | Retardos derivados nuevos en el registro de asistencia.   |
| `derived.updated`    | Retardos derivados cuya cantidad cambió.                  |
| `derived.deleted`    | Retardos derivados que dejaron de aplicar y se retiraron. |

## Errores [#errores]

| Status | Código                        | Causa                                                                                                                      |
| ------ | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `400`  | —                             | Cuerpo mal formado, o `events` vacío o con más de 100 filas.                                                               |
| `400`  | `INVALID_INPUT`               | Alguna fila no pasó validación (`timestamp` sin zona horaria, `direction` fuera de `in`/`out`, etc.). No se registra nada. |
| `400`  | `EMPLOYEE_NOT_FOUND`          | El `employee_number` no existe en la organización.                                                                         |
| `400`  | `NO_ACTIVE_EMPLOYMENT`        | El empleado no tiene empleo activo o pendiente.                                                                            |
| `401`  | —                             | API key ausente o inválida.                                                                                                |
| `403`  | —                             | La API key no tiene el permiso **Registrar checadas**.                                                                     |
| `409`  | `RETRO_CONFIRMATION_REQUIRED` | Algún día cae en un periodo cerrado. Reenvía con `confirm_retro: true`.                                                    |

Los errores de fila llegan con el índice dentro de `events` y los datos de la checada, para que puedas identificarla:

```json
{
  "message": "Validation failed for 1 event(s). No events were ingested.",
  "summary": { "total": 2, "with_errors": 1 },
  "errors": [
    {
      "index": 1,
      "event": {
        "employee_number": 9999,
        "timestamp": "2026-06-05T18:03:00-06:00"
      },
      "issues": [
        {
          "field": "employee_number",
          "code": "EMPLOYEE_NOT_FOUND",
          "message": "That 'employee_number' does not exist in this organization."
        }
      ]
    }
  ]
}
```