API DE INTEGRACIONES / v0.0.1-draft
Una referencia para
conectar con Fichacora.
Consulta empresas, empleados, horarios y jornadas desde una integración de servidor.
Código a61f5e3 · 11 de noviembre de 2026
En desarrollo. Acceso cerrado.
La documentación es pública; la API todavía no está disponible para terceros. No hay alta automática, credenciales de prueba ni sandbox público. Beta mantiene las rutas de integración deshabilitadas y devuelve 404. El primer acceso será por invitación, tras completar las garantías del piloto.
Este borrador describe seis consultas de lectura. El contrato puede cambiar antes del piloto. Escrituras, webhooks, OAuth, sincronización incremental y exportación para nóminas quedan fuera de esta referencia.
Autenticación desde servidor
La credencial identifica una integración y su empresa. Se envía exclusivamente por HTTPS en Authorization: Bearer <token>. Cada consulta requiere el permiso indicado. No incluyas tokens en URLs, aplicaciones cliente ni logs. Esta página no recoge credenciales.
Prefijo previsto: /api/v1/integraciones. El dominio del sandbox se comunicará con la invitación. El dominio .invalid del ejemplo es deliberadamente inoperativo.
curl 'https://sandbox.example.invalid/api/v1/integraciones/employees?per_page=50&page=1' -H 'Authorization: Bearer <TOKEN_DEL_SANDBOX>' -H 'Accept: application/json'
Ejemplo ilustrativo, con datos ficticios:
{"data":[{"id":1,"first_name":"Persona","last_name":"Ejemplo"}],
"meta":{"current_page":1,"per_page":50,"total":1,"last_page":1}}Las listas paginadas usan 50 elementos por defecto, máximo 100. No hay snapshot ni cursor de cambios: modificaciones concurrentes pueden desplazar elementos entre páginas. No hay feed de bajas ni garantía de sincronización completa.
GET
/me
Identidad de la integración y empresa asociada a la credencial. El cliente no selecciona el tenant.
Permiso: context:read
Sin parámetros de consulta.
Esquema de respuesta 200
{
"type": "object",
"properties": {
"integration": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"scopes": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
},
"minItems": 0,
"maxItems": 0
}
]
},
"expires_at": {
"type": [
"string",
"null"
]
}
},
"required": [
"id",
"name",
"scopes",
"expires_at"
]
},
"tenant": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"slug": {
"type": "string"
},
"name": {
"type": "string"
}
},
"required": [
"id",
"slug",
"name"
]
}
},
"required": [
"integration",
"tenant"
]
}GET
/employees
Solo empleados activos, ordenados por apellidos y nombre. No devuelve PIN, iCard ni datos de contacto.
Permiso: employees:read
| Parámetro opcional | Tipo y límites |
|---|---|
per_page | {"type": "integer", "minimum": 1, "maximum": 100, "default": 50} |
page | {"type": "integer", "minimum": 1, "default": 1} |
Esquema de respuesta 200
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
}
},
"required": [
"id",
"first_name",
"last_name"
]
}
},
"meta": {
"type": "object",
"properties": {
"current_page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total": {
"type": "integer"
},
"last_page": {
"type": "integer"
}
},
"required": [
"current_page",
"per_page",
"total",
"last_page"
]
}
},
"required": [
"data",
"meta"
]
}GET
/employee-schedules
Horario asignado para date; por defecto, hoy según la aplicación. horario puede ser null. days contiene horas, minutos de descanso y día laborable. No es el calendario efectivo: no aplica por sí solo ausencias, festivos ni períodos de actividad.
Permiso: employees:read
| Parámetro opcional | Tipo y límites |
|---|---|
employee_id | {"type": "integer", "minimum": 1} |
date | {"type": "string", "format": "date"} |
per_page | {"type": "integer", "minimum": 1, "maximum": 100, "default": 50} |
page | {"type": "integer", "minimum": 1, "default": 1} |
Esquema de respuesta 200
{
"type": "object",
"properties": {
"date": {
"type": "string"
},
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"employee_id": {
"type": "integer"
},
"horario": {
"type": [
"object",
"null"
],
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"color": {
"type": [
"string",
"null"
]
},
"days": {
"type": "string"
}
},
"required": [
"id",
"name",
"color",
"days"
]
}
},
"required": [
"employee_id",
"horario"
]
}
},
"meta": {
"type": "object",
"properties": {
"current_page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total": {
"type": "integer"
},
"last_page": {
"type": "integer"
}
},
"required": [
"current_page",
"per_page",
"total",
"last_page"
]
}
},
"required": [
"date",
"data",
"meta"
]
}GET
/locations
Sedes de empresa, ordenadas por nombre. Devuelve id, name y address.
Permiso: locations:read
| Parámetro opcional | Tipo y límites |
|---|---|
per_page | {"type": "integer", "minimum": 1, "maximum": 100, "default": 50} |
page | {"type": "integer", "minimum": 1, "default": 1} |
Esquema de respuesta 200
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"address": {
"type": "string"
}
},
"required": [
"id",
"name",
"address"
]
}
},
"meta": {
"type": "object",
"properties": {
"current_page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total": {
"type": "integer"
},
"last_page": {
"type": "integer"
}
},
"required": [
"current_page",
"per_page",
"total",
"last_page"
]
}
},
"required": [
"data",
"meta"
]
}GET
/time-entries
Jornadas ordenadas por entrada descendente y después id. from y to filtran la fecha de entrada de forma inclusiva, según la zona horaria de la aplicación; no seleccionan por solapamiento. Si se indican ambas, from no puede superar to. salida puede ser null. Entrada menos salida no representa horas efectivas: no se desglosan pausas ni se calculan nóminas.
Permiso: time-entries:read
| Parámetro opcional | Tipo y límites |
|---|---|
employee_id | {"type": "integer", "minimum": 1} |
from | {"type": "string", "format": "date"} |
to | {"type": "string", "format": "date"} |
per_page | {"type": "integer", "minimum": 1, "maximum": 100, "default": 50} |
page | {"type": "integer", "minimum": 1, "default": 1} |
Esquema de respuesta 200
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"employee_id": {
"type": "integer"
},
"entrada": {
"type": [
"string",
"null"
]
},
"entrada_location_id": {
"type": [
"integer",
"null"
]
},
"salida": {
"type": [
"string",
"null"
]
},
"salida_location_id": {
"type": [
"integer",
"null"
]
}
},
"required": [
"id",
"employee_id",
"entrada",
"entrada_location_id",
"salida",
"salida_location_id"
]
}
},
"meta": {
"type": "object",
"properties": {
"current_page": {
"type": "integer"
},
"per_page": {
"type": "integer"
},
"total": {
"type": "integer"
},
"last_page": {
"type": "integer"
}
},
"required": [
"current_page",
"per_page",
"total",
"last_page"
]
}
},
"required": [
"data",
"meta"
]
}GET
/break-types
Tipos de descanso activos, ordenados por nombre. Devuelve id, name, description e is_paid. No paginado. No inicia ni finaliza descansos.
Permiso: time-entries:read
Sin parámetros de consulta.
Esquema de respuesta 200
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"data"
]
}Errores y límites
Los errores usan JSON incluso sin Accept. Incluyen code, message y request_id; el 422 añade errors por campo. Procesa el código y el estado HTTP, no el texto. Las respuestas incluyen X-Request-ID y Cache-Control: no-store.
{"code":"validation_failed","message":"Unprocessable Content",
"request_id":"00000000-0000-4000-8000-000000000001",
"errors":{"per_page":["El máximo permitido es 100."]}}| HTTP | Código |
|---|---|
| 401 | unauthenticated |
| 403 | forbidden |
| 404 | not_found |
| 405 | method_not_allowed |
| 422 | validation_failed |
| 429 | rate_limit_exceeded |
| 500 | internal_error |
| 503 | service_unavailable |
Los límites de desarrollo son 60 peticiones/minuto por IP y 60 por integración; no son cuotas comprometidas para el piloto. El límite de IP cuenta credenciales inválidas. Ante 429, respeta Retry-After y reduce la frecuencia. Ante 401/403, revisa credenciales y permisos antes de reintentar. La suspensión de una API registrada devuelve 503; con rutas deshabilitadas devuelve 404.
Pendiente antes de ofrecer acceso: cuotas definitivas, alcance de datos, política temporal, límites de consulta, sincronización y política de cambios. Todavía no se ofrece una garantía de disponibilidad o compatibilidad estable.