Volver a Huminyv1 · Asistencia

Conecta tu sistema con la asistencia de Huminy

Esta API permite que un PMS, un sistema de nómina, un ERP o un reloj checador físico registren marcaciones, consulten jornadas y sepan quién está en turno. Todos los registros que entran por aquí quedan en la misma bitácora inmutable con folio, huella SHA-256 encadenada y auditoría que usa la app.

POST/api/public/attendance/eventsRegistrar marcación
GET/api/public/attendance/summariesResúmenes de jornada
GET/api/public/attendance/eligibilityQuién está en turno

1. Alta del cliente de integración

En Control de Asistencia → Integraciones, un usuario con rol RH o Admin técnico crea una app y obtiene:

  • client_id — identificador público del sistema externo.
  • client_secret — se muestra una sola vez; Huminy solo guarda su hash y los últimos 4 caracteres.
  • Alcances: attendance:write para enviar marcaciones y attendance:read para consultar jornadas y elegibilidad.

Cada llamada queda registrada con endpoint, estatus, IP y cliente en la bitácora de API, visible para RH.

2. Autenticación con firma HMAC

Toda petición lleva tres encabezados:

x-huminy-client-id: hmy_live_xxxxxxxx
x-huminy-timestamp: 1755523200000       # epoch en MILISEGUNDOS (± 5 min)
x-huminy-signature: sha256=<hmac_hex>

La firma es HMAC-SHA256(client_secret, `$${timestamp}.$${payload}`) en hexadecimal. El payload es el cuerpo crudo en POST, o el query string sin ? en GET. Un cuerpo distinto al firmado invalida la petición.

// Node.js
import crypto from "node:crypto";

function sign(secret, payload) {
  const ts = Date.now(); // milisegundos
  const hmac = crypto.createHmac("sha256", secret).update(ts + "." + payload).digest("hex");
  return { ts, signature: "sha256=" + hmac };
}

const body = JSON.stringify({ employee_number: "1042", event_type: "clock_in" });
const { ts, signature } = sign(process.env.HUMINY_SECRET, body);

await fetch("https://huminy.app/api/public/attendance/events", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-huminy-client-id": process.env.HUMINY_CLIENT_ID,
    "x-huminy-timestamp": String(ts),
    "x-huminy-signature": signature,
    "idempotency-key": crypto.randomUUID(),
  },
  body,
});

3. POST /api/public/attendance/events

Registra una marcación. Requiere alcance attendance:write. Envía idempotency-key para poder reintentar sin duplicar.

{
  "employee_number": "1042",         // requerido
  "event_type": "clock_in",          // clock_in | clock_out | break_start | break_end
  "device_time": "2026-08-18T07:02:11-05:00",
  "latitude": 21.1619,
  "longitude": -86.8515,
  "method": "reloj_checador",
  "external_device_id": "RC-LOBBY-01"
}

Respuesta 201:

{
  "folio": "ALD-2026-000842",
  "event_type": "clock_in",
  "server_time": "2026-08-18T12:02:14.221Z",
  "validation_result": "valido",     // valido | fuera_de_geocerca | retardo | ...
  "work_date": "2026-08-18",
  "timezone": "America/Cancun"
}

Errores: 401 firma inválida o vencida, 403 alcance insuficiente, 404 colaborador no encontrado, 409 secuencia inválida (por ejemplo salida sin entrada), 422 campos inválidos.

4. GET /api/public/attendance/summaries

Resúmenes diarios calculados: horas trabajadas, descansos, retardos y tiempo extra. Ideal para nómina. Requiere attendance:read.

GET https://huminy.app/api/public/attendance/summaries?from=2026-08-01&to=2026-08-15

{
  "from": "2026-08-01", "to": "2026-08-15", "count": 210,
  "items": [{
    "employee_number": "1042",
    "work_date": "2026-08-01",
    "first_in": "2026-08-01T12:02:14Z",
    "last_out": "2026-08-01T20:05:03Z",
    "worked_minutes": 452, "break_minutes": 30,
    "late_minutes": 2, "overtime_minutes": 12,
    "status": "completa"
  }]
}

5. GET /api/public/attendance/eligibility

Responde si un colaborador puede recibir trabajo ahora mismo: está activo, en turno, presente y sin excepción de calendario. Úsalo antes de asignar una habitación, una tarea o un servicio. Requiere attendance:read.

Parámetros opcionales: employee_number, person_id o external_id (ID estable de tu aplicación, nunca el nombre), work_center_id, expected_work_center_id (para detectar centro equivocado), process (p. ej. opc, liner, closer) y limit (máx. 200).

GET https://huminy.app/api/public/attendance/eligibility?employee_number=1042

{
  "checked_at": "2026-08-18T13:20:00.000Z",
  "count": 1,
  "items": [{
    "employee_number": "1042",
    "full_name": "Mariana López",
    "work_center": { "id": "…", "name": "Hotel Aldea Cancún" },
    "timezone": "America/Cancun",
    "work_date": "2026-08-18",
    "eligible": true,
    "codes": [],                     // ver lista de códigos abajo
    "recommended_action": "allow",   // allow | warn | require_supervisor | block
    "presence": "presente",          // sin_registro | presente | en_descanso | salida_registrada
    "on_shift": true,
    "reason": "Disponible en turno",
    "shift": { "name": "Matutino", "start_time": "07:00:00", "end_time": "15:00:00", "crosses_midnight": false },
    "last_event": { "type": "clock_in", "at": "2026-08-18T12:02:14Z" },
    "exception": null
  }]
}

La respuesta nunca se cachea. Cuando eligible es false, el campo codes explica por qué:

  • EMPLOYMENT_INACTIVE relación laboral inactiva · NO_HUMINY_LINK el ID externo no está vinculado
  • NO_CLOCK_IN sin entrada · OUTSIDE_SHIFT fuera de turno · ON_BREAK en pausa
  • WRONG_WORK_CENTER registró en otro centro · OPEN_INCIDENT incidencia abierta
  • PENDING_SYNC (informativo) terminal con registros por sincronizar · APPROVED_EXCEPTION (informativo) excepción autorizada vigente
  • PROVIDER_UNAVAILABLE Huminy no pudo responder (HTTP 503). Si no obtienes respuesta, tu app debe asumir este código.

Contingencia. RH configura por aplicación, y opcionalmente por proceso, qué hacer cuando la persona no es elegible o Huminy no responde: fail_open (informativo), fail_with_warning (advertencia), require_supervisor o fail_closed (estricto). La respuesta trae el modo vigente en contingency_mode y la acción sugerida en recommended_action; tu aplicación decide. Recomendación para TimeShareFy: guarda en caché el último modo recibido y, ante una caída, aplícalo localmente para no perder una venta.

6. Webhooks salientes

Huminy también puede avisar a tu sistema cuando ocurre algo. Registra una suscripción con la URL destino y los eventos que te interesan; cada entrega se firma igual que las peticiones entrantes y se reintenta ante fallos.

POST https://tu-sistema.com/hooks/huminy
x-huminy-event: attendance.clock_in
x-huminy-event-id: 6f1c…           // único por evento; se conserva en reenvíos
x-huminy-timestamp: 1793182800000   // epoch ms
x-huminy-signature: sha256=<hmac>[,sha256=<hmac con secreto anterior>]

{
  "event_id": "6f1c…",
  "event": "attendance.clock_in",
  "sent_at": "2026-10-29T13:00:00.000Z",
  "data": { "employee_number": "1042", "event_type": "clock_in", "folio": "ALD-2026-000899" }
}

Cómo verificar:

  1. Calcula HMAC-SHA256(secreto, timestamp + "." + cuerpo_crudo) y compáralo (en tiempo constante) con cualquiera de las firmas del encabezado.
  2. Rechaza si x-huminy-timestamp difiere más de 5 minutos de tu reloj (prevención de repetición).
  3. Guarda el event_id y descarta duplicados (idempotencia; los reenvíos manuales reutilizan el mismo ID).
  4. Responde 2xx rápido. Cualquier otra respuesta o un timeout de 5 s cuenta como fallo.

Reintentos: 1 min, 5 min, 30 min, 2 h y 12 h. Tras el sexto intento la entrega pasa a la cola de fallidos, desde donde RH o el equipo técnico pueden reenviarla. Rotación: al rotar el secreto, el anterior sigue firmando en paralelo durante 24 horas para que actualices tu sistema sin cortes.

Catálogo de eventos:

employee.created · employee.updated · employee.transferred · employee.terminated · employee.enabled_for_app · employee.disabled_for_app · attendance.clock_in · attendance.clock_out · attendance.break_started · attendance.break_ended · attendance.late_detected · attendance.absence_detected · attendance.overtime_detected · attendance.event_corrected · attendance.incident_created · attendance.incident_approved · attendance.incident_rejected · shift.assigned · shift.changed · device.connected · device.disconnected · device.sync_failed · compliance.rule_published · certification.expiring · certification.expired · training.completed · attendance.event.created · compliance.finding.created

7. Buenas prácticas

  • Guarda el client_secret en variables de entorno; nunca en el cliente ni en el repositorio.
  • Reintenta con el mismo idempotency-key: Huminy no duplicará el evento.
  • Sincroniza el reloj de tu servidor (NTP): la firma expira a los 5 minutos.
  • Los eventos son inmutables: una corrección se registra como ajuste con motivo, nunca borrando.
  • Solicita únicamente los alcances que tu sistema necesita.

¿Necesitas un adaptador específico (QR, NFC, biométrico, WhatsApp) o mapeo de plantilla desde tu nómina? Escríbenos desde el panel de RH y lo configuramos contigo.