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) ysent_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:200con"duplicate": truey el que consta. Si ese fichaje está pendiente de revisión, es un202con"duplicate": truey 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, el200trae ese; en otro, cuenta el envío: lo registrado a mano se anula (sigue a la vista) y el200con"duplicate": truetrae el fichaje registrado con elclient_uuidenviado. - Ubicación (
latitude,longitude,accuracy): solo si la empresa la tiene activada (geolocation_modeen/config); si no, se descarta. Conobligatoria, un fichaje en el momento sin ubicación recibe422. - 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 comonull. Si no cumple ese formato, se descarta: el fichaje se registra igual y la respuesta trae"verification_discarded": true(nunca422); 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/configo 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 es200con"duplicate": truey el que consta. Sinknown_mspasa 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:
422es definitivo (enseña el motivo);401,429,5xxo sin red, conserva el fichaje y reinténtalo con el mismoclient_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 /ficharresponde403(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