Documentation Index
Fetch the complete documentation index at: https://mintlify.com/fredy-rizo/MultiSas/llms.txt
Use this file to discover all available pages before exploring further.
MultiSas uses subscription-based feature gating to control which parts of the API a company can access. The active plan is stored on the Company document in the available_plans field, and it is enforced at the route level by the TokenValidationPlan middleware. Attempting to call a route that requires a higher plan than the one the company holds results in an immediate HTTP 403 response — no data is read or written.
Available Plans
MultiSas offers four plan tiers. Prices are in Colombian Pesos (COP) per month.
| Plan | Price (COP/month) | Key Features |
|---|
| Plan Básico | $29,000 | Standard electronic invoicing (XML + PDF), client/product/supplier management, basic document storage, 1 user, 1 company, basic chat support |
| Plan Profesional | $59,000 | Everything in Básico + credit & debit notes, basic inventory (stock in/out), general sales reports, multi-user access, query API, increased storage, standard support |
| Plan Premium | $119,000 | Everything in Profesional + advanced inventory (kardex, cost tracking, multi-warehouse), accounts receivable, multi-company panel, full create & query API, advanced roles & permissions, expanded storage, custom PDF templates, priority support |
| Plan Personalizado | Custom | Tailored to the customer’s specific requirements — price negotiated with the Super Admin |
Feature Gates
Plan enforcement is keyed on named features defined in plan.json. Each feature maps to the list of plans that include it. The TokenValidationPlan middleware receives the feature name and checks whether the company’s current available_plans value appears in that list.
| Feature key | Included in |
|---|
facturacion | Plan Basico, Plan Profesional, Plan Premium |
inventario_basico | Plan Profesional, Plan Premium |
inventario_avanzado | Plan Premium |
multiempresa | Plan Premium |
The full features object from plan.json:
{
"features": {
"facturacion": ["Plan Basico", "Plan Profesional", "Plan Premium"],
"inventario_basico": ["Plan Profesional", "Plan Premium"],
"inventario_avanzado":["Plan Premium"],
"multiempresa": ["Plan Premium"]
}
}
Plan Types
The type_available_plans field records how a plan was purchased, which determines how expiration is calculated:
| Value | Meaning |
|---|
Mensual | Month-to-month subscription |
Anual | Annual subscription — months_quantity holds the number of months purchased |
Vacio | No active plan type (default for new companies or after expiration) |
Permanente | Non-expiring — reserved exclusively for the Super Admin account |
The months_quantity field (a Number) records how many months were purchased at sign-up, and day_available_plans records the exact date and time the plan was activated.
Plan Expiration
The expired_available_plans field stores the plan’s end date as a DD/MM/YYYY string. On every authenticated request to a plan-protected route, the check_plan_expiration middleware runs before the feature gate:
- It reads
expired_available_plans from the Company document.
- It parses the date and calculates how many days remain (
days_left = ceil((expiredDate - today) / 86_400_000)).
- If
days_left > 0, it sets req.user.day_available_plans to the remaining day count and calls next().
- If
days_left <= 0, it immediately runs the following update on the Company document and then calls next() (the feature gate will then block the request):
await Company.updateOne(
{ _id: user._id },
{
$set: {
available_plans: "Sin Plan",
type_available_plans: "Vacio",
months_quantity: 0,
day_available_plans: "",
expired_available_plans: "",
},
}
);
When a plan expires, available_plans is automatically reset to Sin Plan on the very next API request that triggers check_plan_expiration — there is no grace period. All feature-gated routes will begin returning HTTP 403 immediately. The company must contact the Super Admin to renew their plan.
Enforcing Plans in Code
The TokenValidationPlan(feature) middleware is added to any route that requires a specific plan. It reads req.user.available_plans (populated by the Token or TokenAny middleware that ran earlier in the chain) and cross-references it against plan.features[feature].
// notesRoutes.js — credit note creation requires the 'facturacion' feature
router.post(
'/credit/:company_id/:sale_id/:production_id/:client_id',
Token,
TokenAuthorize('Admin', 'Super Admin'),
TokenValidationPlan('facturacion'),
create_credit_note
);
If the company’s plan is not in the allowed list for that feature, the middleware short-circuits the request:
// HTTP 403
{
"msj": "Tu plan no permite usar esta funcion",
"status": false
}
The complete middleware chain for a plan-gated, role-protected route is:
TokenAny → TokenAuthorize(...roles) → check_plan_expiration → TokenValidationPlan(feature) → controller
TokenAny must always come first so that req.user is populated before TokenAuthorize or TokenValidationPlan attempt to read it.