Saltar al contenido
AccessTime

API REST v1

Actualizado el 18 de octubre de 2026

La API REST de AccessTime es la misma que usa nuestra app móvil: todo lo que hace la app pasa por aquí. También puedes usarla para pequeñas integraciones propias, por ejemplo consultar los fichajes del día desde un script o solicitar una ausencia desde otra herramienta.

La URL base es https://tudominio/api/v1, todas las peticiones y respuestas son JSON y las rutas protegidas requieren la cabecera Authorization: Bearer {token}.

1. Consigue un token

La autenticación usa tokens personales (Laravel Sanctum), sin cookies ni sesión. El token se obtiene con el email y la contraseña del empleado, los mismos con los que entra al panel web:

curl -X POST https://tudominio/api/v1/login \
  -H "Content-Type: application/json" \
  -d '{ "email": "empleado@empresademo.com", "password": "********" }'

La respuesta incluye el token y los datos básicos del usuario:

{
  "token": "1|abcdef...",
  "user": { "id": 1, "name": "...", "email": "...", "role": "empleado", "company_id": 2 }
}

Guarda el token en un lugar seguro y no lo compartas: da acceso a los datos de esa persona. Si las credenciales no son válidas recibirás un 422, y el endpoint está limitado a 10 intentos por minuto.

Para cerrar sesión, POST /api/v1/logout (autenticado) revoca el token actual.

Los tokens caducan a los 90 días: la respuesta del login trae la fecha en token_expires_at y, pasada, la API responde 401 y hay que volver a pedir uno. Si envías device_name en el login («iPhone de Ana»), el token aparece con ese nombre en Perfil → Dispositivos con acceso, desde donde la persona puede revocarlo.

2. Endpoints disponibles

Método Ruta Qué hace
POST /login Devuelve un token a partir de email y contraseña (con la verificación en dos pasos activada, un reto)
POST /login/two-factor Canjea el reto y el código de verificación por el token
POST /logout Revoca el token actual
GET /config Hora del servidor, zona horaria, geolocalización de la empresa y lo que puede hacer la persona
POST /fichar Registra un fichaje (entrada o salida)
GET /fichajes/hoy Fichajes del día actual
GET /fichajes/semana Fichajes de la semana actual
GET /resumen/mensual?month=YYYY-MM Resumen mensual de horas trabajadas
GET /ausencias Últimas 50 ausencias del usuario
POST /ausencias Solicita una ausencia
DELETE /ausencias/{id} Anula una ausencia propia, con las reglas del panel
GET /ausencias/tipos Tipos de ausencia activos de la empresa
GET /ausencias/saldo?year=YYYY Saldo anual de vacaciones
GET /historial-cambios Quién ha cambiado los datos de la persona, cuándo y con qué rol
POST · DELETE /devices Registra o retira el móvil para las notificaciones push

Todos los endpoints operan sobre el usuario autenticado: cada persona ve solo sus fichajes, sus ausencias y su saldo. Los mensajes (message) llegan en el idioma de la persona, y cada 403 lleva un code que dice qué pasa: solo_lectura, empresa_bloqueada, cuenta_desactivada, email_no_verificado o sin_empresa.

3. Fichar desde la API

Cada fichaje lleva un client_uuid: un UUID que generas una vez, antes del primer intento, y repites en cada reintento. Así, si se corta la red y reintentas, el fichaje nunca se duplica (sin él, 422). Manda también type (in o out); si lo omites, alterna según el último fichaje, igual que el botón del panel (cómo funciona el fichaje).

curl -X POST https://tudominio/api/v1/fichar \
  -H "Authorization: Bearer 1|abcdef..." \
  -H "Content-Type: application/json" \
  -d '{ "client_uuid": "3f2504e0-4f89-41d3-9a0c-0305e82c3301", "type": "in" }'

Respuesta 201 con el fichaje (status, id, type, happened_at…). Un reintento con el mismo client_uuid devuelve 200 y el mismo cuerpo.

  • La hora es la del servidor. Lo capturado sin conexión se reenvía con offline: true, captured_at (la hora del móvil al pulsar) y sent_at (la del móvil al enviar): el servidor corrige el desfase del reloj. Si llega más de 72 horas después, queda como incidencia pendiente de revisión (202): no se pierde. Si lo primero que consta después de su hora, a menos de 5 minutos, es un fichaje del mismo tipo (la persona lo repitió en el kiosco o en el panel mientras esperaba en la cola), es el mismo fichaje repetido: 200 con "duplicate": true y el que consta. Si ese fichaje está pendiente de revisión, es un 202 con "duplicate": true y esa incidencia. Y si lo que consta a menos de 5 minutos es un fichaje del mismo tipo registrado a mano después de que se pulsara (un olvido aprobado o una corrección aceptada antes de que llegara la cola): en el mismo minuto, el 200 trae ese; en otro, cuenta el envío: lo registrado a mano se anula (sigue a la vista) y el 200 con "duplicate": true trae el fichaje registrado con el client_uuid enviado.
  • Ubicación (latitude, longitude, accuracy): solo si la empresa la tiene activada (geolocation_mode en /config); si no, se descarta. Con obligatoria, un fichaje en el momento sin ubicación recibe 422.
  • Verificación en el móvil (verification, opcional): solo el resultado de la que hizo el propio teléfono, nunca datos biométricos: {"method": "biometric_local" | "pin" | "none", "verified": true | false, "os_auth_type": "face" | "fingerprint" | "credential" | null}. Es lo que declara tu aplicación: el servidor no puede comprobarlo. Se recortan los espacios y un texto vacío cuenta como null. Si no cumple ese formato, se descarta: el fichaje se registra igual y la respuesta trae "verification_discarded": true (nunca 422); otras claves se ignoran. Queda en el fichaje y la persona la ve en «Mi registro».
  • known_ms (opcional): la hora en milisegundos del último fichaje que conocía tu aplicación al pulsar, la del servidor (la de /config o la de la última respuesta), no la del móvil. Si después consta otro del mismo tipo que el servidor ya tenía al pulsar (por ejemplo, en el kiosco) o que tu aplicación mandó antes desde su cola sin conexión, se registra el que toca, como hace el botón del panel, salvo que ese fichaje sea de hace menos de 5 minutos y no venga de esa cola: es el mismo fichaje repetido y la respuesta es 200 con "duplicate": true y el que consta. Sin known_ms pasa lo mismo si ese fichaje del mismo tipo, de hace menos de 5 minutos, se hizo por otro canal (el kiosco o el panel); pero el botón desfasado de más tiempo no se corrige, así que mándalo siempre.
  • Errores: 422 es definitivo (enseña el motivo); 401, 429, 5xx o sin red, conserva el fichaje y reinténtalo con el mismo client_uuid.

Los fichajes se guardan en UTC y las consultas de "hoy" y "semana" usan la zona horaria de tu empresa. El resumen mensual se calcula a partir de las jornadas consolidadas cada noche, las mismas que alimentan los informes.

Si la prueba gratuita de la empresa ha terminado sin suscripción, la cuenta está en solo lectura y POST /fichar responde 403 (solo_lectura). Los endpoints de consulta siguen funcionando (planes y facturación).

4. Ausencias desde la API

Puedes consultar los tipos disponibles, el saldo y solicitar una ausencia:

curl -X POST https://tudominio/api/v1/ausencias \
  -H "Authorization: Bearer 1|abcdef..." \
  -H "Content-Type: application/json" \
  -d '{ "absence_type_id": 1, "starts_on": "2026-08-03", "ends_on": "2026-08-07", "reason": "Vacaciones" }'

La API aplica las mismas validaciones que el panel: solapamientos con otras ausencias propias, saldo disponible en los tipos que descuentan y que el rango contenga días laborables (descuenta fines de semana y festivos). Si el tipo requiere aprobación, la solicitud entra en el flujo de aprobaciones con estado pending; si no, queda aprobada directamente. Más detalle en ausencias y vacaciones.

Los tipos de ausencia que exigen justificante no se pueden solicitar por API: la petición devuelve un error y hay que hacerla desde el panel web, donde sí se puede adjuntar el documento.

¿Y ahora qué?

  • Si aún no tienes la cuenta montada, empieza por primeros pasos.
  • Para fichar en una tablet compartida no necesitas la API: usa el kiosco QR.

¿No encuentras lo que buscas?

Escríbenos desde la página de contacto y te ayudamos directamente.

Ir a contacto