Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Arthurr23/XHealtXperience/llms.txt

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

XHealtXperience is built around a strict multi-tenant model: every clinic that signs up gets its own siloed environment — its own database, its own users, its own patients, and its own configuration. No clinic can ever see or touch another clinic’s data. This isolation is enforced at the infrastructure level using the stancl/tenancy package (v3.x) and a path-based tenant identification strategy.

How tenancy works: the two-database model

XHealtXperience maintains two distinct database layers that never overlap.

Central database

The central database is the application’s backbone. It is defined by config/tenancy.php as the mysql connection and stores:
TablePurpose
tenantsOne row per registered clinic, including subscription metadata
domainsPath-based “domains” (tenant IDs) linked to each tenant
usersCentral users only — the Super Admin lives here
jobsLaravel queued jobs
cacheApplication-level cache
sessionsCentral session storage
Nothing from a clinic’s day-to-day operations (patients, appointments, staff) ever touches the central database.

Tenant databases

Each time a new clinic is registered, the platform provisions a dedicated SQLite (or MySQL) database named tenant{id}.sqlite (or tenant{id} in MySQL). This database contains a full, isolated schema including:
  • users — clinic staff accounts (doctors, receptionists, administrators, auditors)
  • pacientes — patient records with clinical files
  • citas — appointments and scheduling
  • servicios, lineas_servicio, servicio_etapas — the clinic’s service catalogue
  • paciente_servicios, paciente_servicio_etapas — patient service execution and tracking
  • quirofanos, salas, procedimientos_quirurgicos — surgical suite management
  • audit_logs — full audit trail for HIPAA-style compliance
  • tenant_settings — per-clinic security and configuration (see Clinic Settings)
  • roles, permissions — Spatie roles and permissions seeded at clinic creation
// config/tenancy.php — database naming convention
'database' => [
    'central_connection' => 'mysql',
    'prefix' => 'tenant',
    'suffix' => '.sqlite',
],

Path-based tenancy and routing

XHealtXperience uses InitializeTenancyByPath — not subdomain-based tenancy. This means every tenant-scoped URL is prefixed with the clinic’s ID:
# Central routes (Super Admin panel, auth)
https://app.xhealthxperience.com/panel-global/dashboard
https://app.xhealthxperience.com/login

# Tenant routes (clinic-specific)
https://app.xhealthxperience.com/{tenantId}/dashboard
https://app.xhealthxperience.com/{tenantId}/login
https://app.xhealthxperience.com/{tenantId}/pacientes
All tenant routes live in routes/tenant.php and are wrapped by the InitializeTenancyByPath middleware:
// routes/tenant.php
Route::middleware([
    'web',
    \Stancl\Tenancy\Middleware\InitializeTenancyByPath::class,
])->group(function () {
    // All tenant routes go here
});
When a request arrives at /{tenantId}/..., the middleware reads the first URL segment, looks up the matching tenant in the central domains table, and switches the active database connection to that clinic’s isolated database. Every subsequent Eloquent query in the request lifecycle runs against the tenant’s own database.

Central routes

Central routes are defined in routes/web.php and carry no tenant prefix. The Super Admin panel is protected by auth, two_factor, and role:Super Admin middleware:
Route::middleware(['auth', 'two_factor', 'role:Super Admin'])
    ->prefix('panel-global')
    ->group(function () {
        Route::get('/dashboard', [SuperAdminController::class, 'dashboard']);
        // ...
    });

The Tenant model

The App\Models\Tenant class extends Stancl\Tenancy\Database\Models\Tenant and declares custom columns so the package treats them as real table columns rather than JSON data:
public static function getCustomColumns(): array
{
    return [
        'id',
        'nombre_clinica',
        'codigo_clinica',
        'email_contacto',
        'telefono_contacto',
        'plan',
        'limite_cuentas',
        'estado',
        'fecha_inicio_suscripcion',
    ];
}
ColumnTypeDescription
idstringURL-safe slug used as the path prefix (e.g. clinica-norte)
nombre_clinicastringHuman-readable display name
codigo_clinicastring(5)Short prefix (e.g. STL) used to generate sequential patient codes
email_contactostringBilling/contact email
telefono_contactostring(20)Optional contact phone
planenumSubscription plan (basico, pro, premium, personalizado)
limite_cuentasunsignedIntegerMaximum number of staff accounts the clinic may create
estadoenumOperational status (activa or suspendida)
fecha_inicio_suscripciondateSubscription start date

Subscription plans

Each plan carries a suggested limite_cuentas value, defined in Tenant::LIMITES_POR_PLAN. The Super Admin may override this number at any time for any clinic.
public const LIMITES_POR_PLAN = [
    'basico'        => 5,
    'pro'           => 20,
    'premium'       => 50,
    'personalizado' => 5, // Starting value — operator adjusts manually
];
PlanDefault account limitIntended for
basico5Small single-doctor practices
pro20Mid-size multi-specialty clinics
premium50Large clinic networks
personalizadoConfigurable (1–1000)Special contracts

Clinic status (estado)

A clinic’s estado column accepts exactly two values enforced by a database CHECK constraint:
  • activa — the clinic is operational; staff can log in and use all features.
  • suspendida — the clinic is suspended; the Super Admin can toggle this from the dashboard at any time.
The only valid values are activa and suspendida (both lowercase, feminine form in Spanish). Using activo or any other variant will trigger a CHECK constraint violation.

Account usage helpers

The Tenant model exposes two helper methods for tracking how many staff accounts a clinic has consumed. Both methods require tenancy to already be initialized for the target tenant, because User lives in the tenant database.
// Returns the current number of user accounts in this clinic's DB
public function cuentasUsadas(): int
{
    return User::count();
}

// Returns how many new accounts can still be created
public function cuentasDisponibles(): int
{
    return max(0, $this->limite_cuentas - $this->cuentasUsadas());
}
These are used by the Super Admin dashboard to display per-clinic capacity metrics and enforce account limits when new users are created.

Reserved tenant IDs

The following IDs can never be used as a tenant id because they conflict with central application routes:panel-global · login · logout · register · apiThe SuperAdminController::store() method enforces this with a Rule::notIn() validation rule. Attempting to register a clinic with any of these IDs will return a validation error.

Super Admin Panel

Manage clinic registration, account limits, suspension, and cross-clinic user creation from the central panel.

Clinic Settings

Configure per-clinic session timeouts, login lockout thresholds, and the visitor self check-in token.

Build docs developers (and LLMs) love