Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Glemynart/SaaS/llms.txt

Use this file to discover all available pages before exploring further.

La Oficina Nítida models every employee according to the Colombian civil registry standard: four distinct name fields, a typed document identifier drawn from Colombia’s official catalog, a work-schedule classification, and an optional branch assignment. All employee data is scoped to a single tenant — no record is ever visible across organizations — and every mutating operation is logged to the platform audit trail.

Employee Data Model

The Four-Part Name Structure

Colombian law registers natural persons with up to four name components. La Oficina Nítida stores each component in its own field rather than in a single free-text string:
FieldRequiredDescription
primerNombreFirst given name
segundoNombreSecond given name (optional)
primerApellidoFirst (paternal) surname
segundoApellidoSecond (maternal) surname (optional)
Keeping names disaggregated enables reliable sorting, searching, and document generation. The search query parameter on GET /employees matches independently against every name component and the document number, so a partial match on any part of the name returns the correct result. Constructing a full name must always be done through the formatNombreCompleto() utility exported from @saas/types:
import { formatNombreCompleto } from '@saas/types';

const fullName = formatNombreCompleto({
  primerNombre: 'María',
  segundoNombre: 'Camila',
  primerApellido: 'Rodríguez',
  segundoApellido: 'López',
});
// → "María Camila Rodríguez López"
The function filters out blank or null segments and joins the remaining parts with a single space. It is the only approved way to build a display name anywhere in the platform — frontend or backend.

Document Identity (tipoDocumento)

Every employee carries a structured document identifier. The tipoDocumento field accepts one of seven enum values corresponding to official Colombian identity documents:
ValueDocument
CCCédula de Ciudadanía
CECédula de Extranjería
TITarjeta de Identidad
PAPasaporte
NITNúmero de Identificación Tributaria
PEPPermiso Especial de Permanencia
PPTPermiso de Protección Temporal
The cedula value (the document number itself) is unique per tenant: creating two employees with the same number under the same organization returns 400 Bad Request. If a soft-deleted employee already holds that number, the API surfaces a specific message asking you to reactivate the existing record instead.

Work Schedule (jornadaLaboral)

ValueMeaning
COMPLETAFull working day
MEDIAHalf working day
POR_HORASHourly schedule

Employee Status (estado)

ValueMeaning
ACTIVOCurrently employed
INACTIVOInactive (set automatically on soft-delete)
SUSPENDIDOEmployment temporarily suspended
RETIRADOEmployment terminated; motivoRetiro and fechaRetiro should be populated

Other Key Fields

  • cargo — Job title (required). Stored as a snapshot inside each contract so the historical record is preserved even if this field changes later.
  • salario — Base salary in Colombian pesos (minimum 0).
  • tipoContrato — Default contract type associated with this employee record (e.g., TERMINO_FIJO, PRESTACION_SERVICIOS).
  • fechaIngreso — Hire date as an ISO date string (required).
  • sedeId — Optional UUID reference to a Sede. Must belong to the same tenant; the API validates this cross-tenant constraint on every write.
  • email — Optional. An empty string is coerced to undefined by the DTO transformer so the field is never stored as an empty string.
  • salarioIntegral — Boolean flag for integral salary (salario integral). Defaults to false.
  • municipioExpedicionDocId — DANE municipality code for the document issuance location.

CRUD Operations

All endpoints are protected by JwtAuthGuard and ActiveTenantGuard. The tenant is resolved automatically from the authenticated JWT — you never pass tenantId in the request body.
POST, PATCH, and DELETE additionally require the ADMIN or OPERADOR role. DELETE is restricted to ADMIN only. GET operations are available to all roles including VIEWER.

Create an employee

POST /employees
Content-Type: application/json

{
  "cedula": "1020304050",
  "tipoDocumento": "CC",
  "primerNombre": "Carlos",
  "segundoNombre": "Andrés",
  "primerApellido": "Pérez",
  "segundoApellido": "Gómez",
  "cargo": "Docente de Primaria",
  "salario": 1800000,
  "tipoContrato": "TERMINO_FIJO",
  "fechaIngreso": "2024-01-15",
  "jornadaLaboral": "COMPLETA",
  "sedeId": "a1b2c3d4-..."
}
Returns the created employee record with 201 Created.

List employees (paginated)

GET /employees?page=1&limit=10&search=Carlos&estado=ACTIVO
Returns a paginated envelope:
{
  "data": [...],
  "meta": {
    "total": 48,
    "page": 1,
    "limit": 10,
    "totalPages": 5
  }
}
Each employee object includes a sedeRef shape ({ id, nombre }) for display purposes.

Get a single employee

GET /employees/:id
Returns the full employee record including sedeRef. Returns 404 if the employee does not exist within the authenticated tenant.

Update an employee

PATCH /employees/:id
Content-Type: application/json

{
  "cargo": "Coordinador Académico",
  "salario": 2200000
}
All UpdateEmployeeDto fields are optional. The service validates that any new cedula does not collide with another active employee in the same tenant. Updating sedeId re-validates tenant membership.

Delete an employee (soft delete)

DELETE /employees/:id
Sets deletedAt to the current timestamp and transitions estado to INACTIVO. Employees are never physically removed — their full history (contracts, expediente, alerts) remains intact.

Bulk Import via Excel

For initial onboarding or large roster updates, La Oficina Nítida provides an atomic Excel import workflow. Requires the ADMIN role.

Step 1 — Download the template

GET /employees/import/template
Returns a file download with Content-Disposition: attachment; filename="plantilla_empleados.xlsx". The template contains all required and optional columns with the correct headers and validation hints.

Step 2 — Fill in the data

Populate the spreadsheet following the column definitions. Key points:
  • Sede resolution — Enter the sede name as a plain text string. The importer looks up the Sede by name within the tenant. If no matching sede is found, that row fails validation.
  • Date format — Use ISO dates (YYYY-MM-DD).
  • Maximum 500 rows per file. Files exceeding this limit are rejected before any processing begins.
  • File size limit — 5 MB maximum.

Step 3 — Upload and import

POST /employees/import
Content-Type: multipart/form-data

file=@plantilla_empleados_filled.xlsx
The import is atomic: the service validates every row first. If any row contains an error (missing required field, duplicate cedula, unknown sede name), the entire import is aborted and a structured error report is returned. No partial writes occur. A successful response reports the number of employees created and includes a per-row summary.
The maximum import size is 500 rows per file. Files with more rows are rejected immediately. For large initial loads, split the data into multiple files and import sequentially.

Search and Filtering

GET /employees accepts the following query parameters via EmployeeQueryDto:
ParameterTypeDefaultDescription
searchstringCase-insensitive partial match against primerNombre, segundoNombre, primerApellido, segundoApellido, cedula, and email
estadoEmpleadoEstadoFilter by status: ACTIVO, INACTIVO, SUSPENDIDO, or RETIRADO
pagenumber1Page number (1-based)
limitnumber10Records per page
Results are always ordered by createdAt descending (most recent first). Soft-deleted employees (deletedAt IS NOT NULL) are excluded from all list responses.
Always use formatNombreCompleto() from @saas/types when you need to display a person’s full name — in UI components, document generation, alert descriptions, or audit messages. Manual string concatenation of the four name fields is not permitted anywhere in the platform. The legacy nombre and apellido fields were removed in FASE 4B and must not be re-introduced.

Build docs developers (and LLMs) love