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 opcionalTipo 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 opcionalTipo 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 opcionalTipo 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 opcionalTipo 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."]}}
HTTPCódigo
401unauthenticated
403forbidden
404not_found
405method_not_allowed
422validation_failed
429rate_limit_exceeded
500internal_error
503service_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.