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.

This guide walks you through cloning the monorepo, starting the NestJS API, registering your organization as a tenant, authenticating, and creating your first employee record. By the end you will have a live access token and a confirmed multi-tenant workspace ready for contracts, digital files, and alerts.
Prerequisites before you begin:
  • PostgreSQL ≥ 16 running and accessible (see the Docker Compose tip below for a one-command local setup).
  • Node.js ≥ 20 installed on your machine.
  • pnpm installed globally (npm install -g pnpm).
  • Redis running locally or via Docker if you plan to use BullMQ job queues (optional for the quickstart steps below).
1

Clone the repo and install dependencies

Clone the repository and install all workspace packages with a single pnpm install from the monorepo root. Then copy the environment template to create your local .env file.
git clone <your-repo-url> oficina-nitida
cd oficina-nitida
pnpm install
cp .env.example .env
Open .env and verify the values for your environment. The defaults from .env.example are:
# Database connection (Prisma / PostgreSQL)
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/saas_db?schema=public"

# Application configuration (NestJS)
PORT=4000

# Redis configuration (Required for BullMQ)
REDIS_URL="redis://localhost:6379"

# Security (Required for JWT auth)
JWT_SECRET="super-secret-key-change-me-in-production"
JWT_ACCESS_EXPIRES_IN="15m"
JWT_REFRESH_EXPIRES_IN="7d"
Replace JWT_SECRET with a strong random value before running in any shared or production environment. The default value in .env.example is a placeholder only.
Once your .env is configured, run the Prisma migrations to initialize the database schema:
pnpm --filter api exec prisma migrate deploy
2

Start the API

Start the NestJS API in development mode using the api workspace filter. The server listens on the port defined by the PORT variable — 4000 by default.
pnpm --filter api dev
You should see NestJS bootstrap output ending with:
[NestApplication] Nest application successfully started
The API will be available at http://localhost:4000. CORS is pre-configured to allow requests from http://localhost:3000 (the Next.js frontend). To run the frontend in parallel, open a second terminal and run:
pnpm --filter web dev
3

Register your organization

Every workspace in La Oficina Nítida is a tenant — a fully isolated organization. Registration creates both the tenant record and the first ADMIN user in a single atomic transaction.
curl -X POST http://localhost:4000/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "nombreTenant": "Colegio San Andrés",
    "nit": "900123456",
    "digitoVerif": "7",
    "razonSocial": "Colegio San Andrés S.A.S.",
    "email": "admin@colegiosanandres.edu.co",
    "passwordPlain": "Segura123!",
    "nombre": "María",
    "apellido": "González"
  }'
A successful response returns the newly created tenant and admin user:
{
  "tenant": {
    "id": "clx1a2b3c4d5e6f7g8h9i0j1k",
    "nombre": "Colegio San Andrés",
    "nit": "900123456"
  },
  "user": {
    "id": "clx1a2b3c4d5e6f7g8h9i0j1l",
    "email": "admin@colegiosanandres.edu.co",
    "nombre": "María",
    "apellido": "González",
    "rol": "ADMIN"
  }
}
Registration is rate-limited to 3 requests per minute per IP. The nit must be unique across the platform — attempting to register the same NIT twice returns 409 Conflict.
4

Log in and get your access token

Use the tenantNit (not the tenant name) together with your email and password to authenticate. Login is multi-tenant aware — the same email address can exist in different tenants without conflict.
curl -X POST http://localhost:4000/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "tenantNit": "900123456",
    "email": "admin@colegiosanandres.edu.co",
    "passwordPlain": "Segura123!"
  }'
A successful login returns a short-lived access token and a long-lived refresh token:
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "dXNlcklkOnNlY3JldA==",
  "user": {
    "id": "clx1a2b3c4d5e6f7g8h9i0j1l",
    "email": "admin@colegiosanandres.edu.co",
    "nombre": "María",
    "apellido": "González",
    "rol": "ADMIN",
    "tenantId": "clx1a2b3c4d5e6f7g8h9i0j1k",
    "tenantNombre": "Colegio San Andrés"
  }
}
The accessToken expires in 15 minutes. Use POST /auth/refresh with the refreshToken (valid for 7 days) to obtain a new pair without re-entering credentials. Login is rate-limited to 5 requests per minute per IP.Save the accessToken — you will use it as the Authorization: Bearer header in all subsequent requests.
5

Create your first employee

With a valid access token, create an employee record. The Colombian name model requires primerNombre and primerApellido as mandatory fields; segundoNombre and segundoApellido are optional. The document type must be one of the official TipoDocIdentidad enum values: CC, CE, TI, PA, NIT, PEP, or PPT.
curl -X POST http://localhost:4000/employees \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <YOUR_ACCESS_TOKEN>" \
  -d '{
    "primerNombre": "Carlos",
    "segundoNombre": "Andrés",
    "primerApellido": "Ramírez",
    "segundoApellido": "Torres",
    "cedula": "1023456789",
    "tipoDocumento": "CC",
    "cargo": "Docente de Primaria",
    "salario": 1800000,
    "tipoContrato": "TERMINO_FIJO",
    "fechaIngreso": "2025-01-15"
  }'
The tenantId is never sent in the request body — it is always extracted from the JWT automatically. This guarantees that an employee can only ever be created within the authenticated tenant’s workspace.
To assign the employee to a branch, include the optional sedeId field with the UUID of an existing Sede record. Sede management is available at GET /sedes once you have created at least one branch for your tenant.
Use Docker Compose for a zero-config local database. The repository ships with a docker-compose.yml that starts a PostgreSQL 16 container pre-configured with the default credentials from .env.example. Run it with:
docker compose up -d
The container exposes PostgreSQL on host port 5433 (mapped from container port 5432) to avoid conflicts with a locally installed PostgreSQL instance. Update your DATABASE_URL accordingly if using Docker:
DATABASE_URL="postgresql://postgres:postgres@localhost:5433/saas_db?schema=public"
Stop and remove the container with docker compose down. Data persists in the postgres_data named volume between restarts.

Build docs developers (and LLMs) love