Referencia de API
- Base URL
- Autenticación
- Formatos de Datos
- Estructura de Endpoints
- Endpoints por Rol
- Account (Usuario Autenticado)
- Reservas (Account)
- GET /reservations
- GET /reservations/:id
- POST /reservations
- PUT /reservations/:id
- DELETE /reservations/:id
- GET /account/bookings
- POST /account/bookings
- GET /account/bookings/calculate-price
- GET /account/bookings/{bookingId}
- GET /account/courts
- GET /account/reservation-types
- GET /account/payment-types
- GET /account/stats
- Perfil (Account)
- GET /account/profile
- PUT /account/profile
- Cliente (Account)
- GET /account/client
- PATCH /account/client
- POST /account/client
- Clases (Account)
- GET /account/classes
- GET /account/classes/my-enrollments
- GET /account/classes/{id}
- POST /account/classes/{id}/enroll
- Admin (Administradores y Operadores)
- Tickets (Panel)
- Reservas (Admin)
- GET /admin/booking
- GET /admin/booking/full
- POST /admin/booking
- GET /admin/booking/{id}
- PATCH /admin/booking/{id}
- DELETE /admin/booking/{id}
- DELETE /admin/booking
- GET /admin/booking/calculate-price
- GET /admin/booking/{id}/price
- POST /admin/booking/validate
- GET /admin/booking/coupons
- POST /admin/booking/{id}/coupon
- DELETE /admin/booking/{id}/coupon
- PATCH /admin/booking/{id}/contract
- Pagos de Reservas (Admin)
- Comprobantes de pago (captura en reserva)
- GET /admin/payments
- GET /admin/payments/stats
- GET /admin/payments/:id
- GET /admin/payments/transaction/:transaction_id
- GET /admin/booking/{bookingId}/payment
- POST /admin/booking/{bookingId}/payment
- GET /admin/booking/{bookingId}/payment/{paymentId}
- PATCH /admin/booking/{bookingId}/payment/{paymentId}
- GET /admin/booking/{bookingId}/payment/{paymentId}/foto
- DELETE /admin/booking/{bookingId}/payment/{paymentId}
- DELETE /admin/booking/{bookingId}/payment/{paymentId}/foto
- POST /admin/payments
- PATCH /admin/payments/:id
- DELETE /admin/payments/:id
- Clases (Admin)
- GET /admin/classes
- POST /admin/classes
- GET /admin/classes/{id}
- PUT /admin/classes/{id}
- DELETE /admin/classes/{id}
- PATCH /admin/classes/{id}/cancel
- PATCH /admin/classes/{id}/complete
- Eventos (Admin — panel PMV)
- GET /admin/events
- POST /admin/events
- GET /admin/events/{id}
- DELETE /admin/events/{id}
- GET /admin/events/{id}/participants
- POST /admin/events/{id}/participants
- DELETE /admin/events/{id}/participants/{participantId}
- Tickets (panel)
- Eventos (Admin) — obsoleto
- Notificaciones (Admin)
- POST /admin/notifications/token
- DELETE /admin/notifications/token
- POST /admin/notifications/send
- Trainer (Inscripciones — enrollment)
- Clases (Trainer)
- GET /trainer/classes
- POST /trainer/classes
- GET /trainer/classes/{id}
- PUT /trainer/classes/{id}
- DELETE /trainer/classes/{id}
- PATCH /trainer/classes/{id}/cancel
- PATCH /trainer/classes/{id}/complete
- Reservas (Trainer)
- GET /trainer/bookings
- POST /trainer/bookings
- GET /trainer/bookings/calculate-price
- Eventos (Trainer — inscripciones)
- GET /trainer/events
- POST /trainer/events
- GET /trainer/events/{id}
- DELETE /trainer/events/{id}
- PATCH /trainer/events/{id}/accept
- PATCH /trainer/events/{id}/reject
- PATCH /trainer/events/{id}/attendance
- Alumnos (Trainer)
- GET /trainer/students
- GET /trainer/students/{id}
- POST /trainer/students/{id}/assign
- Clientes (Trainer)
- GET /trainer/clients
- POST /trainer/clients
- GET /trainer/clients/{id}
- Canchas (Trainer)
- GET /trainer/courts
- Dashboard (Trainer)
- GET /trainer/dashboard/stats
- Tipos de Documento (Trainer)
- GET /trainer/document-types
- Tipos de Pago (Trainer)
- GET /trainer/payment-types
- Tipos de Reserva (Trainer)
- GET /trainer/reservation-types
- Tarifas de Clase (Trainer)
- GET /trainer/tariff-classes
- GET /trainer/tariff-classes/{id}
- Public (Público)
- Sudo (Super Administradores)
- Pagos (Cuenta de Usuario)
- GET /account/payments
- GET /account/payments/:id
- POST /account/payments
- Clientes (Admin)
- GET /admin/clients
- POST /admin/clients
- GET /admin/clients/{id}
- PATCH /admin/clients/{id}
- DELETE /admin/clients/{id}
- Canchas (Admin)
- GET /admin/courts
- POST /admin/courts
- GET /admin/courts/{id}
- PATCH /admin/courts/{id}
- DELETE /admin/courts/{id}
- Tipos de Cancha (Admin)
- GET /admin/court-types
- POST /admin/court-types
- GET /admin/court-types/{id}
- PATCH /admin/court-types/{id}
- DELETE /admin/court-types/{id}
- Tipos de Documento (Admin)
- GET /admin/document-types
- POST /admin/document-types
- GET /admin/document-types/{id}
- PATCH /admin/document-types/{id}
- DELETE /admin/document-types/{id}
- Tipos de Pago (Admin)
- GET /admin/payment-types
- POST /admin/payment-types
- GET /admin/payment-types/{id}
- PATCH /admin/payment-types/{id}
- DELETE /admin/payment-types/{id}
- Estados de Reserva (Admin)
- GET /admin/reservation-statuses
- POST /admin/reservation-statuses
- GET /admin/reservation-statuses/{id}
- PATCH /admin/reservation-statuses/{id}
- DELETE /admin/reservation-statuses/{id}
- Tipos de Reserva (Admin)
- GET /admin/reservation-types
- POST /admin/reservation-types
- GET /admin/reservation-types/{id}
- PATCH /admin/reservation-types/{id}
- DELETE /admin/reservation-types/{id}
- Tarifas (Admin)
- GET /admin/tariffs
- POST /admin/tariffs
- GET /admin/tariffs/{id}
- PATCH /admin/tariffs/{id}
- DELETE /admin/tariffs/{id}
- Tarifas de Clase (Admin)
- GET /admin/tariff-classes
- POST /admin/tariff-classes
- GET /admin/tariff-classes/{id}
- PATCH /admin/tariff-classes/{id}
- DELETE /admin/tariff-classes/{id}
- Dashboard (Admin)
- GET /admin/dashboard/stats
- Usuarios (Admin)
- GET /admin/users
- GET /admin/users/{uid}
- Account (Usuario Autenticado)
- Interfaces TypeScript
- Códigos de Estado HTTP
- Manejo de Errores
- Webhooks
Este documento describe la API REST de UnoSportClub implementada en functions/server.js con Express.js.
Base URL
Producción (API Público)
La API está disponible en producción en los siguientes subdominios:
-
Portal Principal:
-
Portal para usuarios finales
-
Panel de Administración:
-
Panel operativo para administradores y operadores
-
Super Administrador:
-
Consola de control para super usuarios (sudo)
Desarrollo Local
Para desarrollo local, la API está disponible en:
-
http://localhost:6100/api- Desarrollo local -
http://localhost:5005/api- Emulador de desarrollo alternativo -
http://localhost:5006/api- Emulador de desarrollo alternativo
O usando el proxy de Angular (configurado en proxy.conf.json):
/api
Nota:
* El servidor backend en producción se ejecuta con PM2 y está balanceado por Caddy.
* La documentación interactiva de la API (Swagger UI) está disponible en:
- Producción: https://app.unosportclub.com.co/api/api-docs
- QA: https://panel.qa.unosportclub.com.co/api/api-docs
- Desarrollo: http://localhost:6100/api/api-docs
* El esquema OpenAPI (JSON) está disponible en:
- Producción: https://app.unosportclub.com.co/api/api-docs.json
- QA: https://panel.qa.unosportclub.com.co/api/api-docs.json
- Desarrollo: http://localhost:6100/api/api-docs.json
* Los endpoints con localhost son exclusivamente para desarrollo local.
* Los endpoints con subdominios de unosportclub.com.co son el API público en producción.
* Esta documentación está basada en el esquema OpenAPI oficial (versión 3.0.0) y las interfaces TypeScript definidas en @cortex-ia-com-co/common.
* Recomendación: Para la documentación más actualizada y completa, consulta el esquema OpenAPI JSON directamente o usa Swagger UI para explorar la API de forma interactiva.
Autenticación
La API soporta dos métodos de autenticación:
Autenticación con OAuth2 local IdP (Usuarios)
Para usuarios autenticados mediante OAuth2 local IdP, el token debe enviarse en el header:
Authorization: Bearer [firebase-id-token]
Este método se usa para usuarios humanos que se autentican mediante OAuth2 local authentication.
Autenticación con API Key (Bots)
Para bots y sistemas automatizados, se puede usar autenticación mediante API Key enviando el valor de BOT_API_KEYS en el header:
X-Auth: [BOT_API_KEYS]
Características de la autenticación con API Key:
* El header debe ser X-Auth (no Authorization)
* El valor debe coincidir exactamente con la variable de entorno BOT_API_KEYS configurada en el servidor
* Otorga permisos completos: operator, admin, sudo, trainer, y bot
* Disponible en las rutas: /account/, /admin/, /trainer/, y /sudo/
* El usuario autenticado tendrá uid: "bot-api-key" y bot: true
Ejemplo de uso:
curl -H "X-Auth: tu-api-key-aqui" https://app.unosportclub.com.co/api/admin/clients
Nota: Algunos endpoints públicos (/public/*) no requieren autenticación.
OAuth2 authorization_code (MCP / Cursor)
Flujo PKCE para clientes públicos (sin client_secret). El login usa la pantalla del panel.
| Método | Ruta | Descripción |
|--------|------|-------------|
| GET | /authorize | Inicia autorización; redirige a {UNOSPORT_PANEL_URL}/login?oauth_state=… |
| POST | /oauth/authorize/complete | Tras login panel; body { "oauth_state": "uuid" }; requiere Bearer usuario con permiso panel |
| POST | /oauth/token | grant_type=authorization_code con code, code_verifier, client_id |
| GET | /.well-known/oauth-protected-resource/mcp | Metadatos RFC 9728 del recurso MCP |
Variables servidor: MCP_OAUTH_CLIENT_ID, MCP_OAUTH_REDIRECT_URIS, UNOSPORT_PANEL_URL.
MCP Streamable HTTP
| Método | Ruta | Auth | Descripción |
|--------|------|------|-------------|
| POST | /mcp | Bearer usuario (scope mcp, permiso panel) | JSON-RPC (p. ej. initialize, tools/list, tools/call); header Accept: application/json, text/event-stream |
| GET | /mcp | Bearer usuario | SSE; header mcp-session-id |
| DELETE | /mcp | Bearer usuario | Cierra sesión MCP |
URL local de desarrollo: http://localhost:5000/mcp (no usar el proxy del panel en /api/mcp).
Requisitos:
-
Token OAuth2 de usuario con scope
mcpy permisopanel(ver OAuth2 authorization_code (MCP / Cursor)). -
Tras
initialize, enviar notificaciónnotifications/initializedantes detools/listotools/call. -
Las respuestas pueden llegar como JSON o como SSE (
event: message+data: {…}); los clientes deben parsear ambos formatos.
Rechaza tokens M2M (sub con prefijo client:). Las máquinas sudo (client_credentials vía /sudo/machines) no aplican a MCP.
Tools disponibles
Las tools devuelven texto JSON en result.content[0].text. Los errores de negocio incluyen { error, message, code } sin lanzar excepción HTTP.
| Tool | Parámetros | Descripción |
|---|---|---|
|
|
Busca clientes ( |
|
|
Detalle de cliente con contratos ( |
|
|
Alta de cliente ( |
|
|
Lista canchas con tipo ( |
|
|
Catálogo de tipos de cancha ( |
|
|
Catálogo de tipos de reserva ( |
|
|
Catálogo de estados de reserva ( |
|
|
Catálogo de tipos de pago ( |
|
|
Lista reservas con filtros ( |
|
|
Valida disponibilidad de slot ( |
|
|
Calcula precio antes de crear ( |
|
|
Precio de reserva existente con cupón/convenio ( |
|
|
Crea reserva pendiente ( |
|
|
Detalle de reserva con cliente, cancha y pagos ( |
|
|
Actualiza reserva ( |
|
|
Pagos de una reserva ( |
|
|
Registra pago ( |
Flujo recomendado (operador panel — 4 pasos)
-
Disponibilidad:
court_list,reservation_type_list→booking_validate→booking_calculate_price→booking_createconcourt_id,checking,checkout(ISO 8601, hora mural Bogotá). -
Cliente:
client_search→ si no existeclient_create→booking_updateconclient_id. -
Pago:
payment_type_list→booking_add_paymentconamountytransaction_id→booking_payment_listpara verificar. -
Confirmación:
booking_read+booking_get_price→reservation_status_list(obtener id Confirmada) →booking_updateconreservation_status_idcuando el total pagado cubre el precio.
Horarios en hora mural Bogotá (ver Fechas y Horas (hora mural)). No enviar checking_date, checking_time, checkout_date, checkout_time; usar checking y checkout en ISO 8601.
Ejemplo (tools/call)
POST /mcp
Authorization: Bearer <access_token>
Accept: application/json, text/event-stream
Mcp-Session-Id: <session_id>
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "booking_validate",
"arguments": {
"court_id": 1,
"checking": "2026-05-27T18:00:00",
"checkout": "2026-05-27T19:00:00"
}
}
}
Formatos de Datos
Fechas y Horas (hora mural)
La convención principal para reservas y eventos del panel es la hora mural de América/Bogotá, no UTC.
Almacenamiento en base de datos
-
Columnas
reservation.checkingyreservation.checkout:TIMESTAMP WITHOUT TIME ZONE(sin offset; valor mural). -
Entrada en
POST/PUT: ISO 8601 oYYYY-MM-DD HH:mm:ssnormalizado pornormalizeTimestampForDben el backend.
Respuesta API (reservas y eventos admin)
Priorizar campos murales devueltos con TO_CHAR:
-
checking_date,checking_time,checkout_date,checkout_time(YYYY-MM-DDyHH24:MI:SS) -
checking,checkoutopcionales comoYYYY-MM-DD HH24:MI:SSsin sufijoZ
Advertencia UI: no interpretar cadenas con Z como UTC al mostrar horarios de cancha. Usar reservation-wall-time en @cortex-ia-com-co/common.
Estructura de Endpoints
La API está organizada por roles y funcionalidades:
-
/account/*: Endpoints para usuarios autenticados (clientes) -
/admin/*: Endpoints para administradores y operadores -
/trainer/*: Endpoints para entrenadores -
/public/*: Endpoints públicos (sin autenticación) -
/sudo/*: Endpoints para super administradores
Endpoints por Rol
Account (Usuario Autenticado)
Los endpoints bajo /account/* permiten a los usuarios autenticados gestionar sus propios recursos.
GET /reservations
Obtiene la lista de reservas.
Query Parameters:
* client_id (opcional): Filtrar por cliente
* operator_id (opcional): Filtrar por operador
* court_id (opcional): Filtrar por cancha
* status (opcional): Filtrar por estado
* from (opcional): Fecha desde (ISO 8601)
* to (opcional): Fecha hasta (ISO 8601)
Response 200:
{
"data": [
{
"id": 1,
"operator_id": 1,
"client_id": 1,
"court_id": 1,
"reservation_type_id": 1,
"checking": "2024-01-15T10:00:00Z",
"checkout": "2024-01-15T12:00:00Z",
"notes": "Cancha 1",
"created_at": "2024-01-10T08:00:00Z",
"updated_at": "2024-01-10T08:00:00Z"
}
],
"total": 1
}
GET /reservations/:id
Obtiene una reserva específica.
Response 200:
{
"data": {
"id": 1,
"operator_id": 1,
"client_id": 1,
"court_id": 1,
"reservation_type_id": 1,
"checking": "2024-01-15T10:00:00Z",
"checkout": "2024-01-15T12:00:00Z",
"notes": "Cancha 1",
"created_at": "2024-01-10T08:00:00Z",
"updated_at": "2024-01-10T08:00:00Z"
}
}
POST /reservations
Crea una nueva reserva.
Request Body:
{
"operator_id": 1,
"client_id": 1,
"court_id": 1,
"reservation_type_id": 1,
"checking": "2024-01-15T10:00:00Z",
"checkout": "2024-01-15T12:00:00Z",
"notes": "Cancha 1"
}
Response 201:
{
"data": {
"id": 1,
"operator_id": 1,
"client_id": 1,
"court_id": 1,
"reservation_type_id": 1,
"checking": "2024-01-15T10:00:00Z",
"checkout": "2024-01-15T12:00:00Z",
"notes": "Cancha 1",
"created_at": "2024-01-10T08:00:00Z",
"updated_at": "2024-01-10T08:00:00Z"
}
}
PUT /reservations/:id
Actualiza una reserva existente.
Request Body:
{
"checking": "2024-01-15T10:30:00Z",
"checkout": "2024-01-15T12:30:00Z",
"notes": "Cancha 1 - Actualizado"
}
Response 200:
{
"data": {
"id": 1,
"operator_id": 1,
"client_id": 1,
"court_id": 1,
"reservation_type_id": 1,
"checking": "2024-01-15T10:30:00Z",
"checkout": "2024-01-15T12:30:00Z",
"notes": "Cancha 1 - Actualizado",
"created_at": "2024-01-10T08:00:00Z",
"updated_at": "2024-01-10T09:00:00Z"
}
}
DELETE /reservations/:id
Elimina una reserva.
Response 204: No Content
Nota importante:
* Estos endpoints solo devuelven reservas del usuario autenticado
* Las reservas incluyen información agregada de pagos (total_paid, total_amount, is_payment_complete, can_add_payment)
* Los pagos dentro de las reservas incluyen tanto status (string) como statusBoolean (boolean) para compatibilidad
* Las reservas incluyen campos checking y checkout para mostrar fecha y hora de inicio y fin
* Los horarios están disponibles en slots de 30 minutos
* Las interfaces TypeScript correspondientes están en AccountBookingInterface y AccountBookingPaymentInterface del paquete @cortex-ia-com-co/common
GET /account/bookings
Obtiene las reservas del usuario autenticado con información detallada de pagos y paginación.
Query Parameters:
* page (opcional, default: 1): Número de página
* limit (opcional, default: 10): Cantidad de resultados por página
Response 200:
{
"bookings": [
{
"id": 1,
"client_id": 1,
"court_id": 1,
"reservation_type_id": 1,
"checking": "2024-01-15T10:00:00Z",
"checkout": "2024-01-15T12:00:00Z",
"notes": "Cancha 1",
"created_at": "2024-01-10T08:00:00Z",
"updated_at": "2024-01-10T08:00:00Z",
"court_name": "Cancha 1",
"reservation_type_name": "Libre",
"total_paid": 50000.00,
"total_amount": 50000.00,
"is_payment_complete": true,
"can_add_payment": false,
"payments": [
{
"id": 1,
"reservation_id": 1,
"payment_type_id": 1,
"payment_type_name": "Efectivo",
"amount": 50000.00,
"transaction_id": "TXN-123456",
"gateway_response": null,
"status": "completed",
"statusBoolean": true,
"date": "2024-01-10T08:00:00Z",
"description": "Pago de reserva"
}
]
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 15,
"totalPages": 2
}
}
Campos de información de pagos:
* total_paid: Suma total de pagos completados asociados a la reserva
* total_amount: Monto total requerido para la reserva
* is_payment_complete: Indica si el pago está completo (total_paid >= total_amount)
* can_add_payment: Indica si se pueden agregar más pagos a la reserva
* payments: Array de pagos asociados con información detallada
Formato dual de status en pagos:
* status: String con valores "pending" o "completed" (para compatibilidad con frontend)
* statusBoolean: Boolean equivalente (false = pending, true = completed)
Response 401:
{
"error": "No autenticado"
}
POST /account/bookings
Crea una nueva reserva para el cliente autenticado.
Request Body:
{
"court_id": 1,
"reservation_type_id": 1,
"checking": "2024-01-15T10:00:00Z",
"checkout": "2024-01-15T12:00:00Z",
"notes": "Cancha 1"
}
Campos requeridos:
* court_id: ID de la cancha (integer)
* reservation_type_id: ID del tipo de reserva (integer)
* checking: Fecha y hora de inicio (ISO 8601)
* checkout: Fecha y hora de fin (ISO 8601)
Campos opcionales:
* notes: Notas adicionales (string, nullable)
Response 201:
{
"id": 1,
"client_id": 1,
"court_id": 1,
"reservation_type_id": 1,
"checking": "2024-01-15T10:00:00Z",
"checkout": "2024-01-15T12:00:00Z",
"notes": "Cancha 1",
"created_at": "2024-01-10T08:00:00Z",
"updated_at": "2024-01-10T08:00:00Z"
}
Response 400: Datos incompletos, fecha inválida o referencia inválida Response 401: No autenticado Response 404: Cliente no encontrado Response 409: Conflicto de reserva (cancha ocupada)
GET /account/bookings/calculate-price
Calcula el precio de una reserva con ajustes de tarifas usando función PostgreSQL.
Query Parameters:
* date (requerido): Fecha de la reserva (YYYY-MM-DD)
* checking (requerido): Hora de inicio (HH:MM)
* checkout (requerido): Hora de fin (HH:MM)
* court_id (requerido): ID de la cancha
* reservation_type_id (opcional): ID del tipo de reserva
* reservation_id (opcional): ID de la reserva (para edición)
Response 200:
{
"court": {
"id": 1,
"name": "Cancha 1",
"base_price": 50000.00
},
"date": "2024-01-15",
"checking": "10:00",
"checkout": "12:00",
"available": true,
"hours": 2.0,
"final_price": 50000.00,
"price_per_hour": 25000.00,
"applied_adjustments": [
{
"tariff_id": 1,
"description": "Descuento nocturno",
"adjustment": "-10%",
"adjusted_price": 45000.00
}
]
}
Campos de respuesta:
* court: Información de la cancha (id, name, base_price)
* date: Fecha de la reserva (YYYY-MM-DD)
* checking: Hora de inicio (HH:MM)
* checkout: Hora de fin (HH:MM)
* available: Indica si la cancha está disponible en ese horario
* hours: Número de horas de la reserva (decimal)
* final_price: Precio final calculado después de aplicar tarifas
* price_per_hour: Precio por hora calculado
* applied_adjustments: Array de ajustes de tarifa aplicados con información detallada
Nota: La función calcula el precio por periodos de 30 minutos aplicando tarifas según rangos de fechas y horas. La interfaz TypeScript correspondiente es CalculatePriceResponseInterface del paquete @cortex-ia-com-co/common.
GET /account/bookings/{bookingId}
Obtiene una reserva específica del usuario autenticado.
Response 200: Similar a GET /account/bookings pero con un solo objeto en lugar de array.
Response 400: ID de reserva inválido Response 401: No autenticado Response 404: Reserva no encontrada o no pertenece al usuario
GET /account/courts
Obtiene canchas disponibles con verificación de disponibilidad.
Query Parameters:
* checking (opcional): Fecha y hora de inicio para verificar disponibilidad (ISO 8601)
* checkout (opcional): Fecha y hora de fin para verificar disponibilidad (ISO 8601)
Response 200: Array de canchas con información de disponibilidad.
GET /account/reservation-types
Obtiene todos los tipos de reserva disponibles.
Response 200: Array de tipos de reserva (ReservationTypeInterface).
GET /account/payment-types
Obtiene todos los tipos de pago disponibles para el cliente.
Response 200: Array de tipos de pago (PaymentTypeInterface).
GET /account/stats
Obtiene estadísticas del usuario autenticado.
Response 200: Objeto con estadísticas del usuario (reservas, pagos, etc.). Response 401: No autenticado Response 403: Acceso denegado
GET /account/profile
Obtiene el perfil del usuario autenticado.
Response 200: Perfil del usuario obtenido exitosamente (OauthUserProfileInterface).
Response 401: No autenticado
Response 403: Acceso denegado
PUT /account/profile
Actualiza el perfil del usuario autenticado.
Request Body:
{
"displayName": "Juan Pérez",
"phone": "+573001234567"
}
Campos opcionales:
* displayName: Nombre de visualización (string)
* phone: Teléfono de contacto (string)
Response 200: Perfil actualizado exitosamente. Response 401: No autenticado Response 403: Acceso denegado
GET /account/client
Obtiene los datos del cliente del usuario autenticado.
Response 200: Objeto ClientInterface del cliente autenticado.
Response 401: No autenticado Response 404: Cliente no encontrado
PATCH /account/client
Actualiza los datos del cliente del usuario autenticado.
Request Body:
{
"firstName": "Juan",
"lastName": "Pérez",
"document": "1234567890",
"documentTypeId": 1,
"address": "Calle 123",
"phone": "+573001234567"
}
Response 200: Cliente actualizado exitosamente.
POST /account/client
Crea un nuevo cliente para el usuario autenticado.
Request Body:
{
"email": "cliente@example.com",
"first_name": "Juan",
"last_name": "Pérez",
"document": "1234567890",
"document_type_id": 1,
"phone": "+573001234567",
"address": "Calle 123"
}
Campos requeridos:
* email: Email del cliente
* first_name: Nombre del cliente
* last_name: Apellido del cliente
* document: Número de documento
* document_type_id: ID del tipo de documento
Response 201: Cliente creado exitosamente. Response 400: Datos incompletos o usuario no encontrado Response 409: Cliente ya existe para este usuario
GET /account/classes
Obtiene lista de clases disponibles para evento.
Query Parameters:
* reservation_type_id (opcional): Filtrar por tipo de reserva. Siempre filtra por gestion=2.
Response 200: Lista de clases disponibles (filtradas por gestion=2).
Nota: Las clases disponibles son aquellas con reservation_type.gestion = 2 (clases de entrenamiento).
GET /account/classes/my-enrollments
Obtiene las inscripciones del cliente actual.
Response 200: Lista de inscripciones del cliente (EventInterface[]).
GET /account/classes/{id}
Obtiene detalles de una clase específica.
Response 200: Detalles de la clase (TrainingClassInterface).
Response 404: Clase no encontrada
POST /account/classes/{id}/enroll
Inscribirse en una clase.
Request Body:
{
"payments": [
{
"paymentTypeId": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456"
}
]
}
Response 201: Evento exitosa. Response 400: Error de validación Response 404: Clase no encontrada Response 409: Ya está inscrito en esta clase
Admin (Administradores y Operadores)
Los endpoints bajo /admin/* permiten la gestión completa del sistema para administradores y operadores.
Tickets (Panel)
Módulo de paquetes de boletos (catálogo, emisión e historial). Sin caja/till en v1.
Catálogo inicial: migración 083_package_strip.sql crea tablas; el seeder estructural 010_tickets.js inserta el programa y 12 paquetes (unit_price). En panel, la matriz de precios se edita en Tarifas → Tarifas de Paquetes (/tariffs/package); los cambios se persisten con PUT /admin/tickets/packages (no se sobrescriben al re-ejecutar el seeder).
-
GET /admin/tickets/programs— programas con catálogo de paquetes -
PUT /admin/tickets/packages— actualizarunit_pricede paquetes ({ packages: [{ id, unit_price }] }) -
POST /admin/tickets/quote— cotización (package_id,ticket_count) -
POST /admin/tickets/sales— emitir paquete (comprador, teléfonos, pago) -
GET /admin/tickets/sales— historial (headersX-Total-Count,X-Limit,X-Offset) -
GET /admin/tickets/sales/{id}— detalle con tirillas y boletos
Reservas (Admin)
Los endpoints de reservas permiten la gestión completa (CRUD) de reservas desde el panel de administración.
Nota importante:
* Las reservas pueden tener un client_id opcional (null para reservas de mantenimiento)
* Las reservas incluyen información de estado (reservation_status_id) y tipo (reservation_type_id)
* Los campos checking y checkout son fechas y horas en formato ISO 8601
* La interfaz TypeScript correspondiente es BookingInterface y BookingDetailInterface del paquete @cortex-ia-com-co/common
GET /admin/booking
Obtiene todas las reservas con filtros opcionales.
Query Parameters:
* client_id (opcional): Filtrar por ID de cliente
* court_id (opcional): Filtrar por ID de cancha
* limit (opcional, default: 100): Límite de resultados
* offset (opcional, default: 0): Offset para paginación
Response 200: Array de reservas (BookingInterface[]).
GET /admin/booking/full
Obtiene reservas con información completa de pagos y precios.
Query Parameters:
* client_id (opcional): Filtrar por ID de cliente
* court_id (opcional): Filtrar por ID de cancha
* start_date (opcional): Filtrar por fecha de inicio (YYYY-MM-DD)
* end_date (opcional): Filtrar por fecha de fin (YYYY-MM-DD)
* active (opcional): Filtrar solo reservas activas (checkout > ahora)
* confirmed (opcional): Filtrar por estado confirmado (true = confirmadas, false = pendientes)
* limit (opcional, default: 1000): Límite de resultados
* offset (opcional, default: 0): Offset para paginación
Response 200: Array de reservas con información completa incluyendo total_amount, total_paid, total_debt.
POST /admin/booking
Crea una nueva reserva.
Request Body:
{
"client_id": 1,
"court_id": 1,
"reservation_type_id": 1,
"reservation_status_id": 1,
"checking": "2024-01-15T18:00:00Z",
"checkout": "2024-01-15T19:00:00Z",
"notes": "Notas adicionales"
}
Campos requeridos:
* court_id: ID de la cancha (integer)
* reservation_type_id: ID del tipo de reserva (integer)
* checking: Fecha y hora de inicio (ISO 8601)
* checkout: Fecha y hora de fin (ISO 8601)
Campos opcionales:
* client_id: ID del cliente (integer, nullable, opcional para reservas de mantenimiento)
* reservation_status_id: ID del estado de reserva (integer, nullable)
* notes: Notas adicionales (string, nullable)
Response 201: Reserva creada exitosamente (BookingInterface).
Response 400: Datos inválidos, incompletos o referencia inválida
Response 401: No autenticado
Response 409: Conflicto de reserva (cancha ocupada)
GET /admin/booking/{id}
Obtiene una reserva por ID con detalles completos.
Response 200: Reserva obtenida exitosamente con detalles completos (BookingDetailInterface).
Response 404: Reserva no encontrada
Nota: BookingDetailInterface incluye información adicional del cliente (client_first_name, client_last_name, client_email, client_phone, client_document, client_address) y de la cancha (court_base_price, court_type_name).
PATCH /admin/booking/{id}
Edita una reserva por ID.
Request Body:
{
"client_id": 1,
"court_id": 1,
"reservation_type_id": 1,
"reservation_status_id": 1,
"checking": "2024-01-15T18:00:00Z",
"checkout": "2024-01-15T19:00:00Z",
"notes": "Notas actualizadas"
}
Response 200: Reserva actualizada exitosamente (BookingInterface).
Response 400: Datos inválidos
Response 401: No autenticado
Response 404: Reserva no encontrada
DELETE /admin/booking/{id}
Elimina una reserva por ID.
Response 204: Reserva eliminada exitosamente. Response 400: ID de reserva inválido Response 401: No autenticado Response 404: Reserva no encontrada
DELETE /admin/booking
Elimina varias reservas.
Request Body:
{
"ids": ["1", "2", "3"]
}
Response 200: Reservas eliminadas exitosamente.
GET /admin/booking/calculate-price
Calcula el precio de una reserva con ajustes de tarifas.
Query Parameters:
* date (requerido): Fecha de la reserva (YYYY-MM-DD)
* checking (requerido): Hora de inicio (HH:MM)
* checkout (requerido): Hora de fin (HH:MM)
* court_id (requerido): ID de la cancha
* reservation_type_id (opcional): ID del tipo de reserva
Response 200:
{
"court": {
"id": 1,
"name": "Cancha 1",
"base_price": 50000.00
},
"checking_date": "2024-01-15",
"checking_time": "18:00:00",
"checkout_date": "2024-01-15",
"checkout_time": "19:00:00",
"available": true,
"hours": 1.0,
"final_price": 50000.00,
"price_per_hour": 50000.00,
"applied_adjustments": []
}
Campos de respuesta:
* court: Información de la cancha (id, name, base_price)
* checking_date: Fecha de inicio (YYYY-MM-DD)
* checking_time: Hora de inicio (HH:MM:SS)
* checkout_date: Fecha de fin (YYYY-MM-DD)
* checkout_time: Hora de fin (HH:MM:SS)
* available: Indica si la cancha está disponible
* hours: Número de horas de la reserva (decimal)
* final_price: Precio final calculado después de aplicar tarifas
* price_per_hour: Precio por hora calculado
* applied_adjustments: Array de ajustes de tarifa aplicados
Response 400: Parámetros inválidos Response 404: Cancha no encontrada Response 500: Error interno del servidor
GET /admin/booking/{id}/price
Calcula el precio de una reserva existente por su ID.
Response 200: Ficha detallada con precio calculado. Estructura similar a GET /admin/booking/calculate-price con campos checking_date, checking_time, checkout_date, checkout_time.
Response 404: Reserva no encontrada
Response 500: Error interno del servidor
POST /admin/booking/validate
Valida fecha y disponibilidad de una reserva.
Request Body:
{
"court_id": 1,
"checking": "2024-01-15T18:00:00Z",
"checkout": "2024-01-15T19:00:00Z",
"reservation_id": null
}
Campos requeridos:
* court_id: ID de la cancha (integer)
* checking: Fecha y hora de inicio (ISO 8601)
* checkout: Fecha y hora de fin (ISO 8601)
Campos opcionales:
* reservation_id: ID de reserva existente (integer, nullable, excluir en validación)
Response 204: Validación exitosa (sin contenido). Response 400: Datos inválidos Response 409: Conflicto - fecha inválida o cancha no disponible
GET /admin/booking/coupons
Listado solo lectura de cupones aplicados a reservas (auditoría de booking_coupon).
Query Parameters:
* page (opcional, default: 1): Número de página (base 1)
* limit (opcional, default: 50, máx. 200): Registros por página
Response 200: Array en el cuerpo (BookingCouponAppliedInterface[]). Metadatos en cabeceras:
-
X-Total-Count: total de registros -
X-Page: índice de página (base 0) -
X-Per-Page: tamaño de página
[
{
"id": 1,
"reservation_id": 82,
"coupon_id": 5,
"coupon_code": "PROMO10",
"coupon_type": "generic",
"applied": 2500.5,
"created_at": "2026-01-15T12:00:00.000Z"
}
]
Interfaz TypeScript: BookingCouponAppliedInterface, BookingCouponListResponseInterface (@cortex-ia-com-co/common).
POST /admin/booking/{id}/coupon
Aplica un cupón a una reserva (exclusivo con convenio). Ver OpenAPI (POST /admin/booking/{id}/coupon).
DELETE /admin/booking/{id}/coupon
Quita el cupón aplicado de una reserva. Devuelve la ficha de precio actualizada.
PATCH /admin/booking/{id}/contract
Asigna o quita el convenio de una reserva (exclusivo con cupón). Ver OpenAPI.
Pagos de Reservas (Admin)
Los endpoints de pagos permiten la gestión completa (CRUD) de pagos desde el panel de administración.
Nota importante:
* El campo status es un boolean: false = pendiente, true = completado
* Los pagos deben estar asociados a una reserva (reservation_id) o a un evento (enrollment_id) al crearse; la vinculación ocurre en el flujo de reserva/inscripción
* El campo payment_type_id es opcional y puede ser null
* La interfaz TypeScript correspondiente es PaymentInterface del paquete @cortex-ia-com-co/common
* Campo foto (string | null): referencia al objeto en S3/R2; no usar como URL pública en el cliente
Comprobantes de pago (captura en reserva)
Almacenamiento privado S3/R2 gestionado por PaymentReceiptStorage (functions/services/payment-receipt-storage.js). El campo payment.foto guarda la referencia interna; el cliente nunca debe abrir esa URL directamente.
| Operación | Endpoint |
|---|---|
Subir al crear pago |
|
Subir o reemplazar |
|
Descargar (proxy) |
|
Eliminar comprobante (bucket + BD) |
|
Eliminar pago (y comprobante si existe) |
|
Panel: en /booking/payment/:id, botón verde Subir (sin comprobante), Ver (modal) y Eliminar comprobante (rojo en el modal). Integraciones usan los mismos endpoints con multipart/form-data.
Reglas multipart:
-
Campo
fotoobligatorio cuandoContent-Typeesmultipart/form-data -
Tipos:
image/jpeg,image/png,image/webp; máximo 5 MB -
Clave en bucket:
payments/booking-{bookingId}/{hash}.{ext} -
Errores:
413(tamaño),422(sin foto o MIME inválido),503(S3 no configurado)
Ejemplo curl (POST con comprobante):
curl -X POST "http://localhost:6000/api/admin/booking/82/payment" \
-H "Authorization: Bearer $TOKEN" \
-H "X-XSRF-TOKEN: $CSRF" \
-b "XSRF-TOKEN=$CSRF" \
-F "amount=1000" \
-F "transaction_id=34375884" \
-F "payment_type_id=26" \
-F "status=false" \
-F "foto=@/ruta/comprobante.jpg;type=image/jpeg"
Ejemplo curl (eliminar comprobante):
curl -X DELETE "http://localhost:6000/api/admin/booking/82/payment/116/foto" \
-H "Authorization: Bearer $TOKEN" \
-H "X-XSRF-TOKEN: $CSRF" \
-b "XSRF-TOKEN=$CSRF"
Ejemplo curl (eliminar pago):
curl -X DELETE "http://localhost:6000/api/admin/booking/82/payment/116" \
-H "Authorization: Bearer $TOKEN" \
-H "X-XSRF-TOKEN: $CSRF" \
-b "XSRF-TOKEN=$CSRF"
Configuración: Almacenamiento de comprobantes. Diagramas: Subida y Visualización y eliminación.
GET /admin/payments
Obtiene la lista de pagos con filtros y paginación.
Query Parameters:
* search (opcional): Búsqueda por transaction_id o description (búsqueda ILIKE)
* status (opcional): Filtrar por estado del pago (boolean: false = pendiente, true = completado)
* reservation_id (opcional): Filtrar por ID de reservación
* limit (opcional, default: 100): Límite de resultados
* offset (opcional, default: 0): Offset para paginación
Nota: Los pagos pueden estar asociados a reservas (reservation_id) o eventos (enrollment_id).
Ordenamiento: Los resultados se ordenan primero por estado (pendientes primero) y luego por fecha descendente.
Response 200:
[
{
"id": 1,
"reservation_id": 1,
"enrollment_id": null,
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"gateway_response": "{\"status\":\"completed\"}",
"status": true,
"date": "2024-01-10T08:00:00Z",
"description": "Pago de reserva"
},
{
"id": 2,
"reservation_id": 2,
"enrollment_id": null,
"payment_type_id": 1,
"amount": 30000.00,
"transaction_id": "TXN-789012",
"gateway_response": null,
"status": false,
"date": "2024-01-11T10:00:00Z",
"description": "Pago pendiente de validación"
}
]
GET /admin/payments/stats
Obtiene estadísticas agregadas de pagos.
Response 200:
{
"total": 150,
"pending": 10,
"completed": 130,
"total_amount": 7500000.00,
"completed_amount": 6500000.00,
"by_status": {
"completed": 130,
"pending": 10,
"failed": 0,
"refunded": 0
}
}
Campos de respuesta:
* total: Total de pagos en el sistema
* pending: Cantidad de pagos pendientes (status = false)
* completed: Cantidad de pagos completados (status = true)
* total_amount: Suma total de todos los pagos
* completed_amount: Suma total de pagos completados
* by_status: Desglose por estado (nota: failed y refunded no están implementados actualmente)
GET /admin/payments/:id
Obtiene un pago específico por ID.
Response 200:
{
"id": 1,
"reservation_id": 1,
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"gateway_response": "{\"status\":\"completed\"}",
"status": true,
"date": "2024-01-10T08:00:00Z",
"description": "Pago de reserva"
}
Response 404:
{
"error": "Pago no encontrado"
}
GET /admin/payments/transaction/:transaction_id
Obtiene un pago por transaction_id del gateway de pago.
Response 200:
{
"id": 1,
"reservation_id": 1,
"enrollment_id": null,
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"gateway_response": "{\"status\":\"completed\"}",
"status": true,
"date": "2024-01-10T08:00:00Z",
"description": "Pago de reserva"
}
Response 404:
{
"error": "Pago no encontrado"
}
GET /admin/booking/{bookingId}/payment
Lista todos los pagos de una reserva.
Response 200: Array de pagos de la reserva (PaymentInterface[]; cada elemento incluye foto).
Response 404: Reserva no encontrada
Response 500: Error interno del servidor
POST /admin/booking/{bookingId}/payment
Crea un nuevo pago para una reserva.
Content-Type: application/json o multipart/form-data.
Request Body (JSON):
{
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"status": false,
"description": "Pago de reserva",
"gateway_response": null
}
Request Body (multipart):
Campos de formulario equivalentes a JSON más el archivo:
-
foto(file, requerido en multipart): comprobante JPEG, PNG o WebP; máximo 5 MB.
La API sube el archivo a S3/R2 (payments/booking-{id}/{hash}.{ext}) y guarda la URL resultante en payment.foto. Sin archivo (JSON), foto queda null.
Campos requeridos:
* amount: Monto del pago (number, decimal)
* transaction_id: ID de transacción del gateway de pago (string)
Campos opcionales:
* payment_type_id: ID del tipo de pago (integer, nullable)
* status: Estado del pago (boolean, default: false)
* description: Descripción del pago (string, nullable)
* gateway_response: Respuesta completa del gateway de pago (string, nullable)
Response 201: Pago creado exitosamente (PaymentInterface, incluye foto si se subió archivo).
Response 400: Datos de pago inválidos (JSON)
Response 413: Archivo de comprobante supera 5 MB
Response 422: Multipart sin foto, tipo MIME no permitido u otros datos inválidos
Response 503: Almacenamiento S3/R2 no configurado
Response 404: Reserva no encontrada
Response 500: Error interno del servidor
Nota: El reservation_id se toma automáticamente del parámetro de ruta bookingId. Diagrama: Subida de comprobante.
GET /admin/booking/{bookingId}/payment/{paymentId}
Obtiene un pago específico de una reserva.
Response 200: Pago obtenido exitosamente (PaymentInterface, incluye foto).
Response 404: Reserva o pago no encontrado
Response 500: Error interno del servidor
PATCH /admin/booking/{bookingId}/payment/{paymentId}
Actualiza un pago de una reserva.
Request Body (JSON):
{
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"status": true,
"description": "Pago actualizado",
"gateway_response": "{\"status\":\"completed\"}"
}
Request Body (multipart):
-
foto(file, requerido en multipart): comprobante JPEG, PNG o WebP; máximo 5 MB. Añade o reemplazapayment.foto. -
Otros campos del pago opcionales en la misma petición (mismos nombres que JSON).
Response 200: Pago actualizado exitosamente (PaymentInterface, incluye foto si se subió archivo).
Response 400: Datos inválidos
Response 413: Archivo de comprobante supera 5 MB
Response 422: Multipart sin foto, tipo MIME no permitido o sin campos para actualizar (JSON)
Response 503: Almacenamiento S3/R2 no configurado
Response 404: Reserva o pago no encontrado
Response 500: Error interno del servidor
Diagrama: Subida de comprobante.
GET /admin/booking/{bookingId}/payment/{paymentId}/foto
Descarga el comprobante de pago asociado al registro. Requiere autenticación (Bearer) y token CSRF (X-XSRF-TOKEN, cookie XSRF-TOKEN). El bucket S3/R2 no es público; este endpoint actúa como proxy autorizado.
Response 200: Cuerpo binario de la imagen (image/jpeg, image/png o image/webp). Cabeceras Cache-Control: private, no-store.
Response 404: Reserva, pago o comprobante no encontrado
Response 503: Almacenamiento S3/R2 no configurado
Response 500: Error al leer el objeto o error interno
Diagrama: Visualización privada del comprobante.
DELETE /admin/booking/{bookingId}/payment/{paymentId}
Elimina un pago de una reserva. Si payment.foto tiene valor, también elimina el comprobante en S3/R2 antes de borrar el registro.
Response 200: Pago eliminado exitosamente. Response 404: Reserva o pago no encontrado Response 422: ID de reserva o pago inválido Response 503: Almacenamiento S3/R2 no configurado (cuando el pago tiene comprobante y storage no está disponible) Response 500: Error interno del servidor
DELETE /admin/booking/{bookingId}/payment/{paymentId}/foto
Elimina el objeto en el bucket S3/R2 (DeleteObject) y pone payment.foto = null. Si el objeto ya no existe en storage, la operación sigue y limpia la referencia en BD.
Response 204: Comprobante eliminado (sin cuerpo). Response 404: Reserva, pago o comprobante no encontrado Response 503: Almacenamiento S3/R2 no configurado Response 500: Error interno del servidor
POST /admin/payments
Crea un nuevo pago. Útil para registrar pagos manuales o sincronizar pagos desde sistemas externos.
Request Body:
{
"reservation_id": 1,
"enrollment_id": null,
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"status": true,
"description": "Pago de reserva",
"gateway_response": "{\"status\":\"completed\"}"
}
Campos requeridos:
* amount: Monto del pago (number)
* transaction_id: ID de transacción del gateway (string)
* status: Estado del pago (boolean)
Campos opcionales:
* reservation_id: ID de la reservación asociada (integer, nullable, mutuamente exclusivo con enrollment_id)
* enrollment_id: ID de la evento asociada (integer, nullable, mutuamente exclusivo con reservation_id)
* payment_type_id: ID del tipo de pago (integer, nullable)
* description: Descripción del pago (string, nullable)
* gateway_response: Respuesta completa del gateway (string, nullable)
Validaciones:
* Debe proporcionarse reservation_id O enrollment_id, pero no ambos
* Si se proporciona reservation_id, debe existir en la tabla reservation
* Si se proporciona enrollment_id, debe existir en la tabla enrollment
* El transaction_id debe ser una cadena no vacía
Response 201:
{
"id": 1,
"reservation_id": 1,
"enrollment_id": null,
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"gateway_response": "{\"status\":\"completed\"}",
"status": true,
"date": "2024-01-10T08:00:00Z",
"description": "Pago de reserva"
}
Response 400:
{
"error": "Datos incompletos",
"message": "amount y transaction_id son requeridos"
}
PATCH /admin/payments/:id
Actualiza un pago existente. Permite actualizar campos operativos; no admite cambiar reservation_id ni enrollment_id (la afiliación se hace en el flujo de reserva/inscripción).
Request Body:
{
"status": true,
"payment_type_id": 1,
"description": "Pago actualizado"
}
Campos actualizables:
* payment_type_id: Cambiar el tipo de pago (integer, nullable)
* status: Actualizar el estado del pago (boolean)
* description: Actualizar la descripción (string, nullable)
Validaciones:
* Si el body incluye reservation_id o enrollment_id, responde 422
* Al menos un campo actualizable debe ser proporcionado
Response 200:
{
"id": 1,
"reservation_id": 2,
"enrollment_id": null,
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"gateway_response": "{\"status\":\"completed\"}",
"status": true,
"date": "2024-01-10T08:00:00Z",
"description": "Pago actualizado"
}
Response 400:
{
"error": "Datos incompletos",
"message": "Debe proporcionar al menos un campo para actualizar"
}
Response 404:
{
"error": "Pago no encontrado"
}
DELETE /admin/payments/:id
Elimina un pago del sistema. Use con precaución, ya que esto elimina permanentemente el registro del pago.
Response 204: No Content (éxito)
Response 404:
{
"error": "Pago no encontrado"
}
GET /admin/classes
Obtiene lista de clases (admin).
Response 200: Lista de clases (TrainingClassInterface[]).
POST /admin/classes
Crea una nueva clase (admin).
Request Body:
{
"user_id": 1,
"title": "Clase de Fútbol",
"description": "Clase de entrenamiento",
"start_time": "2024-01-15T10:00:00Z",
"end_time": "2024-01-15T12:00:00Z",
"start_date": "2024-01-15",
"end_date": "2024-03-15",
"class_start_time": "10:00:00",
"recurrence_days": ["MO", "WE", "FR"]
}
Campos requeridos:
* title: Título de la clase
* start_time: Fecha y hora de inicio
* end_time: Fecha y hora de fin
* start_date: Fecha de inicio del período
* end_date: Fecha de fin del período
* class_start_time: Hora de inicio de cada clase
* recurrence_days: Array de días de recurrencia (MO, TU, WE, TH, FR, SA, SU)
Campos opcionales:
* user_id: ID del entrenador (opcional, usa admin actual)
* description: Descripción de la clase
Response 201: Clase creada exitosamente.
GET /admin/classes/{id}
Obtiene una clase específica (admin).
Response 200: Datos de la clase (TrainingClassInterface).
Response 404: Clase no encontrada
PATCH /admin/classes/{id}/cancel
Cancela una clase (admin).
Response 200: Clase cancelada exitosamente.
PATCH /admin/classes/{id}/complete
Marca una clase como completada (admin).
Response 200: Clase marcada como completada exitosamente.
Eventos (Admin — panel PMV)
Eventos deportivos del panel: reserva de cancha + metadatos + participantes por casilla. No usar los endpoints obsoletos de inscripción (accept/reject/attendance); no existen en v0.4.1.
Desambiguación: xref:api-reference.adoc#_eventos_trainer_inscripciones describe inscripciones a clases (enrollment), no este módulo.
GET /admin/events
Lista eventos ordenados por checking descendente.
Response 200: Array EventInterface[] (cuerpo sin envoltorio).
[
{
"id": 12,
"reservation_id": 340,
"title": "Torneo amistoso",
"event_type_id": 1,
"event_type_name": "Evento",
"capacity": 8,
"status": "scheduled",
"court_id": 2,
"court_name": "Cancha 2",
"checking_date": "2026-06-16",
"checking_time": "19:00:00",
"checkout_date": "2026-06-16",
"checkout_time": "21:00:00",
"checking": "2026-06-16 19:00:00",
"checkout": "2026-06-16 21:00:00",
"participant_count": 3
}
]
POST /admin/events
Crea reserva tipo Evento (estado Pendiente) y fila event.
Request Body:
{
"court_id": 2,
"checking": "2026-06-16 19:00:00",
"checkout": "2026-06-16 21:00:00",
"title": "Torneo amistoso",
"description": "Opcional",
"capacity": 8,
"event_type_id": 1,
"notes": "Notas de reserva"
}
Campos requeridos: court_id, checking, checkout, title, capacity (>= 1), event_type_id.
Response 201: EventInterface con participants: [].
Response 404: Cancha o tipo de evento inexistente.
Response 409: Slot ocupado o solapamiento (validateBookingSlot).
Response 422: Formato de fecha/hora inválido.
GET /admin/events/{id}
Detalle del evento con array participants.
Response 200: EventInterface incluyendo participants: EventParticipantInterface[].
Response 404: Evento no encontrado.
DELETE /admin/events/{id}
Elimina el evento y la reserva asociada (CASCADE).
Response 204: Sin contenido. Response 404: Evento no encontrado.
GET /admin/events/{id}/participants
Lista participantes del evento.
Response 200: EventParticipantInterface[].
POST /admin/events/{id}/participants
Asigna cliente a una casilla consumiendo un boleto.
Request Body:
{
"slot": 1,
"client_id": 42
}
Alternativa con boleto explícito: ticket_id en lugar de client_id (el backend resuelve cliente desde la tirilla).
Response 201: Participante creado.
Response 409: Cliente duplicado, casilla ocupada o ticket ya usado.
Response 422: client_id o ticket_id requerido; slot fuera de capacidad; cliente sin boleto disponible.
DELETE /admin/events/{id}/participants/{participantId}
Quita participante y libera el boleto (status: available).
Response 204: Sin contenido. Response 404: Participante no encontrado.
Tickets (panel)
El listado de emisiones y el wizard de venta están en la ruta panel /tickets, alineados con GET /admin/tickets/sales y POST /admin/tickets/sales (ver sección Tickets más arriba).
Eventos (Admin) — obsoleto
Los siguientes endpoints no existen en v0.4.1 y corresponden al modelo antiguo de inscripciones: PATCH /admin/events/{id}/accept, reject, attendance. Usar participantes y tickets según esta sección.
POST /admin/notifications/token
Registra o actualiza token FCM para usuario (admin).
Request Body:
{
"userId": "firebase-uid-123",
"token": "fcm-token-abc123"
}
Campos requeridos:
* userId: OAuth2 local IdP UID del usuario
* token: Token FCM del dispositivo
Response 200: Token FCM registrado exitosamente. Response 400: Datos incompletos
DELETE /admin/notifications/token
Elimina token FCM del usuario autenticado (admin).
Response 200: Token FCM eliminado exitosamente. Response 404: Token no encontrado
POST /admin/notifications/send
Envia notificación push a un cliente específico.
Request Body:
{
"clientId": 1,
"title": "Título de la notificación",
"body": "Cuerpo de la notificación",
"url": "/reservations/1"
}
Campos requeridos:
* clientId: ID del cliente al que se enviará la notificación
Campos opcionales:
* title: Título de la notificación
* body: Cuerpo de la notificación
* url: URL a la que navegar al hacer clic
Response 200: Notificación enviada exitosamente. Response 400: Datos incompletos Response 404: Cliente o usuario no encontrado
Nota: La interfaz TypeScript correspondiente es PushNotificationPayload del paquete @cortex-ia-com-co/common.
Trainer (Inscripciones — enrollment)
Los endpoints bajo /trainer/* gestionan clases, alumnos e inscripciones (enrollment). No confundir con eventos del panel (EventInterface); ver Módulo de Eventos (Panel).
GET /trainer/classes
Obtiene lista de clases del entrenador.
Response 200: Lista de clases (TrainingClassInterface[]).
POST /trainer/classes
Crea una nueva clase.
Request Body: Similar a POST /admin/classes pero sin user_id (usa el entrenador autenticado).
Response 201: Clase creada exitosamente.
GET /trainer/classes/{id}
Obtiene una clase específica.
Response 200: Datos de la clase (TrainingClassInterface).
Response 404: Clase no encontrada
PATCH /trainer/classes/{id}/complete
Marca una clase como completada.
Response 200: Clase marcada como completada exitosamente.
GET /trainer/bookings
Obtiene lista de reservas del entrenador.
Query Parameters:
* court_id (opcional): Filtrar por cancha específica
* from (opcional): Fecha de inicio para filtrar reservas (ISO 8601)
* to (opcional): Fecha de fin para filtrar reservas (ISO 8601)
Response 200: Lista de reservas (excluye canceladas).
POST /trainer/bookings
Crea una reserva para el entrenador.
Request Body: Similar a POST /account/bookings.
Response 201: Reserva creada exitosamente.
GET /trainer/bookings/calculate-price
Calcula el precio de una reserva con ajustes de tarifas.
Query Parameters:
* date (requerido): Fecha de la reserva (YYYY-MM-DD)
* checking (requerido): Hora de inicio (HH:MM)
* checkout (requerido): Hora de fin (HH:MM)
* court_id (requerido): ID de la cancha
* reservation_type_id (opcional): ID del tipo de reserva
Response 200: Ficha detallada con precio calculado (CalculatePriceResponseInterface).
Response 400: Parámetros inválidos
Response 404: Cancha no encontrada
Response 500: Error interno del servidor
Eventos (Trainer — inscripciones)
En trainer, «eventos» son inscripciones (enrollment) a clases. La interfaz histórica en respuestas puede aparecer como inscripción; no confundir con EventInterface del panel (xref:api-reference.adoc#_eventos_admin_panel).
GET /trainer/events
Obtiene lista de eventos del entrenador.
Query Parameters:
* class_id (opcional): Filtrar eventos por clase
* status (opcional): Filtrar eventos por estado (boolean)
Response 200: Lista de eventos (EventInterface[]).
POST /trainer/events
Crea una nueva evento.
Request Body:
{
"class_id": 1,
"client_id": 1
}
Response 201: Evento creada exitosamente. Response 400: Datos inválidos Response 404: Clase no encontrada
GET /trainer/events/{id}
Obtiene una evento específica.
Response 200: Datos de la evento (EventInterface).
Response 404: Evento no encontrada
DELETE /trainer/events/{id}
Elimina una evento.
Response 200: Evento eliminada exitosamente. Response 400: No se puede eliminar (fecha pasada o no creada por el trainer)
PATCH /trainer/events/{id}/reject
Rechaza una evento.
Request Body:
{
"reason": "Cupo lleno"
}
Response 200: Evento rechazada exitosamente.
PATCH /trainer/events/{id}/attendance
Registra asistencia de un alumno.
Request Body:
{
"attendance_status": "present"
}
Response 200: Asistencia registrada exitosamente.
GET /trainer/students
Obtiene lista de alumnos del entrenador.
Query Parameters:
* class_id (opcional): Filtrar alumnos por clase específica
Response 200: Lista de alumnos (StudentInterface[]).
GET /trainer/students/{id}
Obtiene un alumno específico.
Response 200: Datos del alumno (StudentInterface).
Response 404: Alumno no encontrado
POST /trainer/students/{id}/assign
Asigna un alumno a una clase.
Request Body:
{
"class_id": 1
}
Response 201: Alumno asignado exitosamente. Response 400: Datos inválidos Response 404: Clase o alumno no encontrado
GET /trainer/clients
Obtiene lista de clientes con búsqueda.
Query Parameters:
* search (opcional): Búsqueda por email, display_name, first_name, last_name, document o phone
* limit (opcional, default: 20): Límite de resultados
* offset (opcional, default: 0): Offset para paginación
Response 200: Lista de clientes (ClientInterface[]).
POST /trainer/clients
Crea un nuevo cliente.
Request Body: Similar a POST /admin/clients.
Response 201: Cliente creado exitosamente.
GET /trainer/clients/{id}
Obtiene un cliente específico.
Response 200: Datos del cliente (ClientInterface).
Response 404: Cliente no encontrado
GET /trainer/courts
Obtiene lista de canchas disponibles.
Response 200: Lista de canchas (CourtInterface[]).
GET /trainer/dashboard/stats
Obtiene estadísticas del dashboard del entrenador.
Response 200: Estadísticas del dashboard.
GET /trainer/document-types
Obtiene todos los tipos de documento disponibles.
Response 200: Lista de tipos de documento (DocumentTypeInterface[]).
GET /trainer/payment-types
Obtiene todos los tipos de pago disponibles.
Response 200: Lista de tipos de pago (PaymentTypeInterface[]).
GET /trainer/reservation-types
Obtiene todos los tipos de reserva disponibles.
Response 200: Lista de tipos de reserva (ReservationTypeInterface[]).
Sudo (Super Administradores)
Los endpoints bajo /sudo/* son exclusivos para super administradores con permisos completos.
GET /sudo/users
Obtiene todos los usuarios de OAuth2 local IdP (solo para administradores).
Query Parameters:
* maxResults (opcional, default: 1000): Número máximo de resultados (máximo 1000)
* pageToken (opcional): Token para paginación
Response 200: Lista de usuarios con paginación (UsersListResponseInterface).
Response 403: No tienes permisos de administrador
POST /sudo/users
Crea un nuevo usuario en OAuth2 local IdP.
Request Body:
{
"email": "usuario@example.com",
"password": "password123",
"displayName": "Usuario",
"disabled": false,
"emailVerified": false,
"role": "admin",
"trainer": false
}
Campos requeridos:
* email: Email del usuario
* password: Contraseña del usuario
Campos opcionales:
* displayName: Nombre para mostrar
* disabled: Si el usuario está deshabilitado
* emailVerified: Si el email está verificado
* role: Rol principal ("admin", "operator", "sudo", "trainer")
* trainer: Indica si el usuario tendrá rol de entrenador (boolean)
Response 201: Usuario creado exitosamente (OauthUserProfileInterface).
Response 400: Datos inválidos (email duplicado, formato inválido, etc.)
GET /sudo/users/{uid}
Obtiene un usuario específico de OAuth2 local IdP.
Response 200: Usuario obtenido exitosamente (OauthUserProfileInterface).
Response 404: Usuario no encontrado
PUT /sudo/users/{uid}
Actualiza un usuario de OAuth2 local IdP.
Request Body: Similar a POST pero con campos opcionales.
Response 200: Usuario actualizado exitosamente.
DELETE /sudo/users/{uid}
Elimina un usuario de OAuth2 local IdP.
Response 204: Usuario eliminado exitosamente. Response 404: Usuario no encontrado
Pagos (Cuenta de Usuario)
Los endpoints de pagos de cuenta permiten a los usuarios autenticados gestionar sus propios pagos asociados a sus reservas.
Nota importante:
* Estos endpoints solo devuelven pagos asociados a reservas del usuario autenticado
* El campo status es un boolean: false = pendiente, true = completado
* Los pagos incluyen información adicional de la reserva asociada (court_name, checking, checkout)
* Validación de pagos para confirmar reserva: Para confirmar una reserva, todos los pagos asociados deben tener status: true (completados) y el monto total pagado debe cubrir el precio total de la reserva
GET /account/payments
Obtiene el historial de pagos del usuario autenticado con paginación.
Query Parameters:
* page (opcional, default: 1): Número de página
* limit (opcional, default: 10): Cantidad de resultados por página
Response 200:
{
"payments": [
{
"id": 1,
"reservation_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"gateway_response": "{\"status\":\"completed\"}",
"status": true,
"date": "2024-01-10T08:00:00Z",
"description": "Pago de reserva",
"court_name": "Cancha 1",
"checking": "2024-01-15T10:00:00Z",
"checkout": "2024-01-15T12:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 25,
"totalPages": 3
}
}
Campos adicionales en respuesta:
* court_name: Nombre de la cancha de la reserva asociada
* checking: Fecha y hora de inicio de la reserva
* checkout: Fecha y hora de fin de la reserva
GET /account/payments/:id
Obtiene un pago específico del usuario autenticado.
Response 200:
{
"id": 1,
"reservation_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"gateway_response": "{\"status\":\"completed\"}",
"status": true,
"date": "2024-01-10T08:00:00Z",
"description": "Pago de reserva",
"court_name": "Cancha 1",
"checking": "2024-01-15T10:00:00Z",
"checkout": "2024-01-15T12:00:00Z"
}
Response 403:
{
"error": "Acceso denegado",
"message": "Este pago no pertenece al usuario autenticado"
}
Response 404:
{
"error": "Pago no encontrado"
}
POST /account/payments
Crea un nuevo pago asociado a una reserva del cliente autenticado. El frontend debe asegurarse de que la reserva ya exista y pertenezca al cliente actual.
Request Body:
{
"reservation_id": 1,
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"description": "Pago de reserva"
}
Campos requeridos:
* reservation_id: ID de la reserva asociada (integer)
* payment_type_id: ID del tipo de pago (integer)
* amount: Monto del pago (number, debe ser mayor a 0)
* transaction_id: ID de transacción del gateway (string, no vacío)
Campos opcionales:
* description: Descripción del pago (string, nullable)
Validaciones:
* La reserva debe existir y pertenecer al cliente del usuario autenticado
* El transaction_id debe ser una cadena no vacía
* El amount debe ser un número mayor a 0
* Los IDs deben ser numéricos válidos
Response 201:
{
"id": 1,
"reservation_id": 1,
"payment_type_id": 1,
"amount": 50000.00,
"transaction_id": "TXN-123456",
"gateway_response": null,
"status": false,
"date": "2024-01-10T08:00:00Z",
"description": "Pago de reserva"
}
Response 400:
{
"error": "Datos incompletos",
"message": "reservation_id, payment_type_id, amount y transaction_id son requeridos"
}
Response 403:
{
"error": "Acceso denegado",
"message": "La reserva no pertenece al usuario autenticado"
}
GET /admin/clients
Obtiene todos los clientes.
Query Parameters:
* search (opcional): Búsqueda por email o display_name
* limit (opcional, default: 100): Límite de resultados
* offset (opcional, default: 0): Offset para paginación
Response 200: Lista de clientes (ClientInterface[]).
POST /admin/clients
Crea un nuevo cliente.
Request Body:
{
"user_id": 1,
"email": "cliente@example.com",
"display_name": "Juan Pérez",
"first_name": "Juan",
"last_name": "Pérez",
"phone": "+573001234567",
"document": "1234567890",
"document_type_id": 1,
"address": "Calle 123"
}
Campos requeridos:
* first_name: Nombre del cliente
* last_name: Apellido del cliente
* document: Número de documento
* document_type_id: ID del tipo de documento
Campos opcionales:
* user_id: ID del usuario existente en la base de datos (user.id). Opcional si se proporciona email
* email: Email del nuevo usuario (opcional si se proporciona user_id). Se creará el usuario en OAuth2 local IdP con contraseña aleatoria y se enviará un email con link para establecer contraseña
* display_name: Nombre para mostrar del usuario (solo si se proporciona email)
* phone: Teléfono del cliente (opcional). Se vinculará al usuario en OAuth2 local IdP
* address: Dirección del cliente
Response 201: Cliente creado exitosamente (ClientInterface).
Response 400: Datos inválidos
GET /admin/clients/{id}
Obtiene un cliente por ID.
Response 200: Cliente encontrado (ClientInterface).
Response 404: Cliente no encontrado
PATCH /admin/clients/{id}
Edita un cliente por ID.
Request Body:
{
"first_name": "Juan",
"last_name": "Pérez",
"document": "1234567890",
"document_type_id": 1,
"address": "Calle 123"
}
Response 200: Cliente actualizado exitosamente.
GET /admin/courts
Obtiene todas las canchas.
Query Parameters:
* search (opcional): Búsqueda por nombre
Response 200: Lista de canchas (CourtInterface[]).
POST /admin/courts
Crea una nueva cancha.
Request Body:
{
"name": "Cancha 1",
"court_type_id": 1,
"cost": 10000.00,
"price": 50000.00
}
Campos requeridos:
* name: Nombre de la cancha
* court_type_id: ID del tipo de cancha
Response 201: Cancha creada exitosamente (CourtInterface).
Response 400: Datos inválidos
GET /admin/courts/{id}
Obtiene una cancha por ID.
Response 200: Cancha encontrada (CourtInterface).
Response 404: Cancha no encontrada
GET /admin/court-types
Obtiene todos los tipos de cancha.
Query Parameters:
* search (opcional): Búsqueda por nombre o descripción
Response 200: Lista de tipos de cancha (CourtTypeInterface[]).
POST /admin/court-types
Crea un nuevo tipo de cancha.
Request Body:
{
"name": "Fútbol",
"description": "Cancha de fútbol"
}
Campos requeridos:
* name: Nombre del tipo de cancha
Response 201: Tipo de cancha creado exitosamente (CourtTypeInterface).
Response 400: Datos inválidos
GET /admin/court-types/{id}
Obtiene un tipo de cancha por ID.
Response 200: Tipo de cancha encontrado (CourtTypeInterface).
Response 404: Tipo de cancha no encontrado
PATCH /admin/court-types/{id}
Actualiza un tipo de cancha.
Response 200: Tipo de cancha actualizado exitosamente.
DELETE /admin/court-types/{id}
Elimina un tipo de cancha.
Response 204: Tipo de cancha eliminado exitosamente.
GET /admin/document-types
Obtiene todos los tipos de documento.
Query Parameters:
* search (opcional): Búsqueda por nombre o descripción
Response 200: Lista de tipos de documento (DocumentTypeInterface[]).
POST /admin/document-types
Crea un nuevo tipo de documento.
Request Body:
{
"name": "Cédula de Ciudadanía",
"description": "Documento de identidad colombiano"
}
Campos requeridos:
* name: Nombre del tipo de documento
Response 201: Tipo de documento creado exitosamente (DocumentTypeInterface).
Response 400: Datos inválidos
GET /admin/document-types/{id}
Obtiene un tipo de documento por ID.
Response 200: Tipo de documento encontrado (DocumentTypeInterface).
Response 404: Tipo de documento no encontrado
PATCH /admin/document-types/{id}
Actualiza un tipo de documento.
Response 200: Tipo de documento actualizado exitosamente.
DELETE /admin/document-types/{id}
Elimina un tipo de documento.
Response 204: Tipo de documento eliminado exitosamente.
GET /admin/payment-types
Obtiene todos los tipos de pago.
Query Parameters:
* search (opcional): Búsqueda por nombre o descripción
Response 200: Lista de tipos de pago (PaymentTypeInterface[]).
POST /admin/payment-types
Crea un nuevo tipo de pago.
Request Body:
{
"name": "Efectivo",
"description": "Pago en efectivo"
}
Campos requeridos:
* name: Nombre del tipo de pago
Response 201: Tipo de pago creado exitosamente (PaymentTypeInterface).
Response 400: Datos inválidos
GET /admin/payment-types/{id}
Obtiene un tipo de pago por ID.
Response 200: Tipo de pago encontrado (PaymentTypeInterface).
Response 404: Tipo de pago no encontrado
PATCH /admin/payment-types/{id}
Actualiza un tipo de pago.
Response 200: Tipo de pago actualizado exitosamente.
DELETE /admin/payment-types/{id}
Elimina un tipo de pago.
Response 204: Tipo de pago eliminado exitosamente.
GET /admin/reservation-statuses
Obtiene todos los estados de reserva.
Query Parameters:
* search (opcional): Búsqueda por nombre o descripción
Response 200: Lista de estados de reserva (ReservationStatusInterface[]).
POST /admin/reservation-statuses
Crea un nuevo estado de reserva.
Request Body:
{
"name": "Confirmada",
"description": "Reserva confirmada",
"color": "#10b981"
}
Campos requeridos:
* name: Nombre del estado
* color: Color hexadecimal para representación visual
Response 201: Estado de reserva creado exitosamente (ReservationStatusInterface).
Response 400: Datos inválidos
GET /admin/reservation-statuses/{id}
Obtiene un estado de reserva por ID.
Response 200: Estado de reserva encontrado (ReservationStatusInterface).
Response 404: Estado de reserva no encontrado
PATCH /admin/reservation-statuses/{id}
Actualiza un estado de reserva.
Response 200: Estado de reserva actualizado exitosamente.
DELETE /admin/reservation-statuses/{id}
Elimina un estado de reserva.
Response 204: Estado de reserva eliminado exitosamente.
GET /admin/reservation-types
Obtiene todos los tipos de reserva.
Query Parameters:
* search (opcional): Búsqueda por nombre o descripción
Response 200: Lista de tipos de reserva (ReservationTypeInterface[]).
POST /admin/reservation-types
Crea un nuevo tipo de reserva.
Request Body:
{
"name": "Libre",
"description": "Reserva libre"
}
Campos requeridos:
* name: Nombre del tipo de reserva
Response 201: Tipo de reserva creado exitosamente (ReservationTypeInterface).
Response 400: Datos inválidos
GET /admin/reservation-types/{id}
Obtiene un tipo de reserva por ID.
Response 200: Tipo de reserva encontrado (ReservationTypeInterface).
Response 404: Tipo de reserva no encontrado
PATCH /admin/reservation-types/{id}
Actualiza un tipo de reserva.
Response 200: Tipo de reserva actualizado exitosamente.
DELETE /admin/reservation-types/{id}
Elimina un tipo de reserva.
Response 204: Tipo de reserva eliminado exitosamente.
GET /admin/tariffs
Obtiene todas las tarifas.
Query Parameters:
* search (opcional): Búsqueda por descripción
* court_id (opcional): Filtrar por cancha
* reservation_type_id (opcional): Filtrar por tipo de reserva
Response 200: Lista de tarifas (TariffInterface[]).
POST /admin/tariffs
Crea una nueva tarifa.
Request Body:
{
"court_id": 1,
"reservation_type_id": 1,
"rrule": null,
"start_date": "2024-01-01",
"end_date": "2024-12-31",
"start_time": "18:00:00",
"end_time": "22:00:00",
"adjustment": "-10%",
"description": "Descuento nocturno",
"is_active": true
}
Campos requeridos:
* adjustment: Ajuste (ej: "-10%", "5000", "-2000", "15%")
* is_active: Si la tarifa está activa (boolean)
Campos opcionales:
* court_id: ID de la cancha (null = todas las canchas)
* reservation_type_id: ID del tipo de reserva (null = todos los tipos)
* rrule: Regla recurrente según RFC 5545 (RRULE)
* start_date: Fecha de inicio del período (YYYY-MM-DD)
* end_date: Fecha de fin del período (YYYY-MM-DD)
* start_time: Hora de inicio del rango horario (HH:MM:SS)
* end_time: Hora de fin del rango horario (HH:MM:SS)
* description: Descripción de la tarifa
Response 201: Tarifa creada exitosamente (TariffInterface).
Response 400: Datos inválidos
GET /admin/tariffs/{id}
Obtiene una tarifa por ID.
Response 200: Tarifa encontrada (TariffInterface).
Response 404: Tarifa no encontrada
GET /admin/tariff-classes
Obtiene todas las tarifas de clase.
Query Parameters:
* court_type_id (opcional): Filtrar por tipo de cancha
* reservation_type_id (opcional): Filtrar por tipo de reserva
Response 200: Lista de tarifas de clase (TariffClassInterface[]).
POST /admin/tariff-classes
Crea una nueva tarifa de clase.
Request Body:
{
"court_type_id": 1,
"reservation_type_id": 2,
"single_price": 25000.00,
"total_price": 50000.00,
"max_capacity": 20
}
Campos requeridos:
* court_type_id: ID del tipo de cancha
* reservation_type_id: ID del tipo de reserva
* single_price: Precio por persona
* total_price: Precio total de la clase
* max_capacity: Capacidad máxima de la clase
Response 201: Tarifa de clase creada exitosamente (TariffClassInterface).
Response 400: Datos inválidos
GET /admin/tariff-classes/{id}
Obtiene una tarifa de clase por ID.
Response 200: Tarifa de clase encontrada (TariffClassInterface).
Response 404: Tarifa de clase no encontrada
PATCH /admin/tariff-classes/{id}
Actualiza una tarifa de clase.
Response 200: Tarifa de clase actualizada exitosamente.
DELETE /admin/tariff-classes/{id}
Elimina una tarifa de clase.
Response 204: Tarifa de clase eliminada exitosamente.
GET /admin/dashboard/stats
Obtiene estadísticas del dashboard.
Response 200: Estadísticas del dashboard (DashboardStatsWithRecentInterface).
Campos de respuesta:
* clients: Estadísticas de clientes (total, verified, unverified)
* courts: Estadísticas de canchas (total)
* reservations: Estadísticas de reservas (total, today, this_week, this_month)
* payments: Estadísticas de pagos (total, total_amount, completed_amount, pending)
* recent_reservations: Array de reservas recientes
* recent_payments: Array de pagos recientes
GET /admin/users
Obtiene usuarios de OAuth2 local IdP (para operadores y administradores).
Query Parameters:
* maxResults (opcional, default: 50): Número máximo de resultados (máximo 1000)
* pageToken (opcional): Token para paginación
Response 200: Lista de usuarios con paginación (UsersListResponseInterface).
Interfaces TypeScript
Todas las interfaces TypeScript están definidas en el paquete @cortex-ia-com-co/common y están disponibles para importar:
import {
BookingInterface,
BookingDetailInterface,
BookingFullInterface,
AccountBookingInterface,
AccountBookingPaymentInterface,
AccountPaymentInterface,
PaymentInterface,
ClientInterface,
TrainingClassInterface,
EventInterface,
TariffClassInterface,
ReservationTypeInterface,
ReservationStatusInterface,
PaymentTypeInterface,
DocumentTypeInterface,
CourtInterface,
CourtTypeInterface,
TariffInterface,
CalculatePriceResponseInterface,
PaginationInterface,
PaginatedResponseInterface,
StudentInterface,
OauthUserProfileInterface,
UserInterface,
UsersListResponseInterface,
DashboardStatsWithRecentInterface,
PushNotificationPayload,
ErrorResponseInterface
} from '@cortex-ia-com-co/common';
Interfaces principales:
Reservas
-
BookingInterface: Estructura base de reservas con campos básicos (id,client_id,court_id,reservation_type_id,checking,checkout,notes, etc.) -
BookingDetailInterface: ExtiendeBookingInterfacecon información completa del cliente (client_first_name,client_last_name,client_email,client_phone,client_document,client_address) y de la cancha (court_base_price,court_type_name), además de pagos asociados -
BookingFullInterface: Reservas con información completa de pagos y precios (total_amount,total_paid,total_debt) -
AccountBookingInterface: Reservas con información de pagos para usuarios autenticados, incluyetotal_paid,total_amount,is_payment_complete,can_add_paymenty array depayments -
AccountBookingPaymentInterface: Estructura de pagos dentro de reservas de cuenta, incluyestatus(string: "pending" | "completed") ystatusBoolean(boolean) para compatibilidad
Pagos
-
PaymentInterface: Estructura de pagos (puede tenerreservation_idoenrollment_id, pero no ambos). El campostatuses boolean (false= pendiente,true= completado). Campo opcionalfoto(string | null): URL interna del comprobante en S3/R2; la descarga al cliente usaGET …/payment/{paymentId}/foto, no la URL almacenada -
AccountPaymentInterface: ExtiendePaymentInterfacecon información adicional de la reserva asociada (court_name,checking,checkout)
Clientes y Usuarios
-
ClientInterface: Estructura de clientes con información completa (id,user_id,email,display_name,first_name,last_name,phone,document,document_type_id,address,email_verified,is_available) -
StudentInterface: Estructura de alumnos del entrenador (id,user_id,email,display_name,first_name,last_name,phone,email_verified,created_at) -
OauthUserProfileInterface: Estructura de usuarios de OAuth2 local IdP con claims de roles (uid,email,emailVerified,displayName,disabled,role,trainer,operator,admin,sudo,metadata) -
UserInterface: Estructura de usuarios en la base de datos (id,local_id,email,email_verified,display_name,phone,disabled,creation_time,last_sign_in_time, etc.) -
UsersListResponseInterface: Respuesta de lista de usuarios con paginación (users,pageToken,total)
Clases y eventos
-
TrainingClassInterface: Clases de entrenamiento (id,user_id,reservation_id,title, horarios, recurrencia,status, etc.) -
EventInterface: Evento deportivo del panel (reservation_id,event_type_id,title,capacity,status, campos muralchecking_*/checkout_*,participantsopcional) -
EventParticipantInterface: Participante por casilla (event_id,client_id,ticket_id,strip_id,slot, datos de cliente) -
CreateEventPayload: Cuerpo dePOST /admin/events(court_id,checking,checkout,title,capacity,event_type_id, …) -
EnrollmentEventInterface(nombre histórico en trainer): inscripción a clase (class_id,client_id,status,attendance_status, pagos asociados)
Canchas y Tipos
-
CourtInterface: Estructura de canchas (id,court_type_id,name,cost,price,court_type_name) -
CourtTypeInterface: Estructura de tipos de cancha (id,name,description,created_at)
Tarifas
-
TariffInterface: Estructura de tarifas con ajustes dinámicos (id,court_id,reservation_type_id,rrule,start_date,end_date,start_time,end_time,adjustment,description,is_active,created_at,updated_at) -
TariffClassInterface: Estructura de tarifas de clase (id,court_type_id,reservation_type_id,single_price,total_price,max_capacity,created_at,updated_at,court_type_name,reservation_type_name)
Tipos y Estados
-
ReservationTypeInterface: Estructura de tipos de reserva (id,name,description) -
ReservationStatusInterface: Estructura de estados de reserva (id,name,color,description). El campocolores un color hexadecimal para representación visual en calendarios -
PaymentTypeInterface: Estructura de tipos de pago (id,name,description) -
DocumentTypeInterface: Estructura de tipos de documento (id,name,description)
Utilidades
-
CalculatePriceResponseInterface: Respuesta del cálculo de precio con información detallada (court,date,checking,checkout,available,hours,price_per_hour,final_price,applied_adjustments) -
PaginationInterface: Información de paginación (page,limit,total,totalPages) -
PaginatedResponseInterface<T>: Respuesta paginada genérica con datos y paginación -
DashboardStatsWithRecentInterface: Estadísticas del dashboard con datos recientes (clients,courts,reservations,payments,recent_reservations,recent_payments) -
PushNotificationPayload: Payload para notificaciones push (clientId,title,body,url) -
ErrorResponseInterface: Estructura de respuestas de error (error,message,stack)
Nota: Todas las interfaces están completamente tipadas y documentadas en el paquete common. Consulta la documentación del paquete para más detalles sobre cada interfaz. Las interfaces están alineadas con el esquema OpenAPI disponible en /api/api-docs.json.
Códigos de Estado HTTP
-
200 OK: Petición exitosa -
201 Created: Recurso creado exitosamente -
204 No Content: Recurso eliminado exitosamente -
400 Bad Request: Error en la petición (validación fallida, datos mal formados) -
Ejemplos:
user_iddebe ser un número válido, email inválido, se requiereuser_idoemail -
401 Unauthorized: No autenticado, token inválido, o API key inválida-
Para OAuth2 local IdP: Token de Firebase inválido o expirado
-
Para API Key: Header
X-Authfaltante o valor deBOT_API_KEYSincorrecto
-
-
403 Forbidden: No autorizado para realizar la acción -
404 Not Found: Recurso no encontrado -
409 Conflict: Conflicto de recursos (recurso ya existe o está en conflicto) -
Ejemplos: Email ya en uso, teléfono ya en uso, usuario ya es cliente, usuario no puede convertirse en cliente
-
500 Internal Server Error: Error del servidor
Nota sobre códigos de estado:
* Los códigos 400 se usan para solicitudes mal formadas o datos inválidos
* Los códigos 409 se usan específicamente para conflictos de recursos (email/teléfono en uso, usuario ya es cliente, etc.)
* Esta separación permite a los clientes distinguir entre errores de validación y conflictos de recursos
Manejo de Errores
Todas las respuestas de error siguen el formato:
{
"error": "Mensaje de error",
"message": "Descripción detallada del error (opcional)"
}
Ejemplo:
{
"error": "Datos incompletos",
"message": "Los campos requeridos no están presentes: court_id, checking"
}
Nota: La interfaz TypeScript correspondiente es ErrorResponseInterface del paquete @cortex-ia-com-co/common.
Webhooks
El sistema expone webhooks para notificaciones de eventos:
POST /webhooks/payment
Webhook para recibir notificaciones de pagos del gateway.
Request Body:
{
"transaction_id": "TXN-123456",
"amount": 50000.00,
"status": "completed",
"gateway_response": "{\"status\":\"completed\"}",
"date": "2024-01-10T08:00:00Z"
}
Nota: El webhook puede recibir status como string desde el gateway (ej: "completed", "pending", "failed"). El sistema convertirá internamente estos valores a boolean según corresponda.
El sistema procesará el webhook y:
1. Buscará si existe un pago con ese transaction_id
2. Si no existe, creará o actualizará el pago en el contexto del flujo de reserva/inscripción
3. Si existe, actualizará el estado del pago según corresponda
Response 200:
{
"status": "processed",
"payment_id": 1
}
Campos de respuesta:
* status: Estado del procesamiento del webhook ("processed", "error", etc.)
* payment_id: ID del pago creado o actualizado