La Oficina Nítida is built multi-tenant from the ground up. Every organization that registers on the platform receives its own isolated data space, enforced at the database level through aDocumentation 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.
tenantId column present on every table. There is no shared data between organizations, no cross-tenant queries, and no administrative backdoor that bypasses this boundary.
How Isolation Works
Tenant isolation operates at three layers simultaneously:- Database layer — Every table (employees, contracts, branches, documents, invoices, and more) carries a
tenantIdUUID as its first indexed column. No query returns data without filtering on that column. - JWT layer — When a user authenticates, the server embeds their
tenantIddirectly into the signed access token payload. All subsequent requests carry this value automatically. - Service layer — Every service method receives
tenantIdextracted from the verified JWT via the@CurrentUser()decorator. It is never read from the request body or query parameters.
where: { tenantId } as its primary filter, ensuring rows from other tenants are structurally unreachable.
Tenant Data Model
TheTenant model is the root entity of the entire system. All other records cascade from it.
| Field | Type | Description |
|---|---|---|
id | String (UUID) | Primary key — used as tenantId throughout all tables |
nombre | String | Display name of the organization |
nit | String (unique) | Colombian tax ID number, without the check digit |
digitoVerif | String | NIT check digit |
razonSocial | String | Legal business name |
plan | Plan enum | Subscription plan: STARTER, PROFESIONAL, or OPERADOR_EDUCATIVO |
activo | Boolean | Whether the tenant can currently operate on the platform |
trialEndsAt | DateTime? | Expiry of the trial period, if applicable |
direccion | String? | Physical address |
ciudad | String? | City |
departamento | String? | Department (Colombian state) |
telefono | String? | Contact phone number |
email | String? | Contact email address |
logoUrl | String? | URL of the uploaded logo in Cloudflare R2 |
representanteLegal | String? | Legal representative’s full name |
firmaRepresentanteUrl | String? | URL of the digitized signature in Cloudflare R2 |
municipioId | String? | FK to the DANE municipality catalog |
aportesExonerados | Boolean | Art. 114-1 ET payroll exemption flag |
Subscription Plans
The platform currently defines three plans via thePlan enum:
| Plan | Intended for |
|---|---|
STARTER | Organizations getting started; default plan assigned at registration |
PROFESIONAL | Growing businesses needing more capacity |
OPERADOR_EDUCATIVO | Colombian educational operators managing multiple campuses, administrative staff, and teachers |
Tenant Registration
A new tenant is created through a single atomic call toPOST /auth/register. The registration creates the Tenant record and the first User (always an ADMIN) inside a single PostgreSQL transaction. If either step fails, neither record is persisted.
409 Conflict.
ActiveTenantGuard
Every protected route in the platform stacksActiveTenantGuard immediately after JwtAuthGuard. Before a request reaches any controller method, the guard calls TenantsService.ensureActive(tenantId), which queries the database and throws BadRequestException if the tenant’s activo field is false.
This means a deactivated organization loses access to all endpoints instantly — no individual user deactivation is necessary. The guard cannot be bypassed because it runs at the NestJS middleware level, before controller logic executes.