openapi: 3.0.3 info: title: OmniOil SCADA API description: | API de la plataforma OmniOil para telemetría IoT/SCADA en el sector de hidrocarburos. Cumple con la Resolución 0651 de la ANH (Agencia Nacional de Hidrocarburos, Colombia). ## Autenticación La mayoría de endpoints requieren un Bearer JWT token en el header `Authorization`. Obtén tu token con `POST /api/auth/login`. ## Versionado La API actual es la versión 1. Todos los endpoints incluyen el header `X-API-Version: 1` en las respuestas. ## Cumplimiento ANH Los endpoints de telemetría e informes cumplen con la normativa colombiana de reporte de producción de hidrocarburos (Resolución ANH 0651). version: "1.0.0" contact: name: OmniOil Support email: soporte@omnioil.app license: name: Propietario servers: - url: /api description: API Server (via proxy) - url: http://localhost:8000/api description: Desarrollo local components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT schemas: LoginRequest: type: object required: [email, password] properties: email: type: string format: email example: admin@omnioil.app password: type: string format: password example: "••••••••" LoginResponse: type: object properties: token: type: string description: JWT access token role: type: string enum: [superadmin, admin, supervisor, operador, anh_reader] projects: type: array items: type: object properties: id: type: string format: uuid name: type: string User: type: object properties: id: type: string format: uuid email: type: string format: email name: type: string role: type: string enum: [superadmin, admin, supervisor, operador, anh_reader] is_active: type: boolean projects: type: array items: type: string format: uuid created_at: type: string format: date-time CreateUserRequest: type: object required: [email, name, role, password] properties: email: type: string format: email name: type: string role: type: string enum: [superadmin, admin, supervisor, operador, anh_reader] password: type: string minLength: 8 EdgeProject: type: object properties: id: type: string format: uuid name: type: string description: type: string location: type: string is_active: type: boolean last_health_report: type: object nullable: true created_at: type: string format: date-time CreateProjectRequest: type: object required: [name] properties: name: type: string example: "Campo Norte - Pozo 01" description: type: string location: type: string EdgeAsset: type: object properties: id: type: string format: uuid project_id: type: string format: uuid name: type: string asset_type: type: string enum: [well, tank, pipeline, station, meter] latitude: type: number format: double nullable: true longitude: type: number format: double nullable: true created_at: type: string format: date-time CreateAssetRequest: type: object required: [project_id, name, asset_type] properties: project_id: type: string format: uuid name: type: string example: "Pozo P-01" asset_type: type: string enum: [well, tank, pipeline, station, meter] latitude: type: number format: double longitude: type: number format: double EdgeDevice: type: object properties: id: type: string format: uuid asset_id: type: string format: uuid name: type: string protocol: type: string enum: [modbus_tcp, modbus_rtu, opc_ua, s7, dnp3] host: type: string nullable: true port: type: integer nullable: true slave_id: type: integer nullable: true enabled: type: boolean CreateDeviceRequest: type: object required: [name, protocol] properties: name: type: string protocol: type: string enum: [modbus_tcp, modbus_rtu, opc_ua, s7, dnp3] host: type: string port: type: integer slave_id: type: integer enabled: type: boolean default: true EdgeVariable: type: object properties: id: type: string format: uuid device_id: type: string format: uuid alias: type: string data_type: type: string enum: [float, int, bool, string] register_address: type: integer nullable: true unit: type: string nullable: true scale_factor: type: number nullable: true enabled: type: boolean CreateVariableRequest: type: object required: [alias, data_type] properties: alias: type: string example: "temperatura_cabezal" data_type: type: string enum: [float, int, bool, string] register_address: type: integer unit: type: string example: "°C" scale_factor: type: number enabled: type: boolean default: true TelemetryPayload: type: object required: [asset_id, readings] properties: asset_id: type: string format: uuid timestamp: type: string format: date-time readings: type: array items: type: object required: [variable, value] properties: variable: type: string example: "presion_cabezal" value: type: number unit: type: string AlarmEvent: type: object properties: id: type: string format: uuid project_id: type: string format: uuid asset_id: type: string format: uuid nullable: true variable: type: string value: type: number alarm_type: type: string enum: [high, low, critical_high, critical_low, comm_loss] message: type: string acknowledged: type: boolean timestamp: type: string format: date-time AlertRule: type: object properties: id: type: string format: uuid project_id: type: string format: uuid name: type: string variable_alias: type: string condition: type: string enum: [gt, lt, gte, lte, eq] threshold: type: number severity: type: string enum: [info, warning, critical] is_enabled: type: boolean created_at: type: string format: date-time CreateAlertRuleRequest: type: object required: [project_id, name, variable_alias, condition, threshold, severity] properties: project_id: type: string format: uuid name: type: string variable_alias: type: string example: "presion_cabezal" condition: type: string enum: [gt, lt, gte, lte, eq] threshold: type: number example: 150.0 severity: type: string enum: [info, warning, critical] BillingUsage: type: object properties: project_id: type: string format: uuid period_start: type: string format: date-time period_end: type: string format: date-time telemetry_points: type: integer active_devices: type: integer plan: type: string enum: [starter, professional, enterprise] estimated_cost_usd: type: number format: double Invoice: type: object properties: id: type: string format: uuid project_id: type: string format: uuid period: type: string example: "2024-01" amount_usd: type: number format: double status: type: string enum: [pending, paid, overdue, cancelled] issued_at: type: string format: date-time paid_at: type: string format: date-time nullable: true ANHReport: type: object properties: id: type: string format: uuid project_id: type: string format: uuid report_date: type: string format: date period_start: type: string format: date-time period_end: type: string format: date-time status: type: string enum: [pending, generated, submitted] hash_sha384: type: string nullable: true created_at: type: string format: date-time HealthResponse: type: object properties: status: type: string enum: [ok, degraded, error] version: type: string example: "1.0.0" timestamp: type: string format: date-time uptime_seconds: type: integer services: type: object properties: database: $ref: '#/components/schemas/ServiceStatus' api: $ref: '#/components/schemas/ServiceStatus' ServiceStatus: type: object properties: status: type: string enum: [ok, error] latency_ms: type: integer nullable: true message: type: string nullable: true ErrorResponse: type: object properties: error: type: string example: "No autorizado" security: - BearerAuth: [] tags: - name: Auth description: Autenticación y gestión de sesiones - name: Users description: Gestión de usuarios (admin) - name: Dashboard description: Datos del dashboard principal - name: Telemetry description: Ingesta y consulta de telemetría - name: ANH Reports description: Reportes de cumplimiento ANH Resolución 0651 - name: Alerts description: Reglas de alerta y alarmas activas - name: Operations description: Operaciones, pruebas y registros ANH 0651 - name: Billing description: Facturación y suscripciones - name: Edge Projects description: Proyectos de telemetría edge - name: Edge Assets description: Activos (pozos, tanques, estaciones) - name: Edge Devices description: Dispositivos de campo (PLCs, RTUs, Modbus) - name: Edge Variables description: Variables y tags de los dispositivos - name: System description: Estado del sistema y métricas paths: # ── Auth ────────────────────────────────────────────────────────────────── /auth/login: post: tags: [Auth] summary: Iniciar sesión security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' responses: '200': description: Login exitoso content: application/json: schema: $ref: '#/components/schemas/LoginResponse' '401': description: Credenciales inválidas content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' '429': description: Demasiados intentos (rate limited) /auth/register: post: tags: [Auth] summary: Registrar nuevo usuario (deshabilitado en producción) security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '201': description: Usuario creado '403': description: Registro deshabilitado en producción '429': description: Rate limited /auth/refresh: post: tags: [Auth] summary: Renovar access token usando refresh token security: [] responses: '200': description: Token renovado content: application/json: schema: $ref: '#/components/schemas/LoginResponse' '401': description: Refresh token inválido o expirado /auth/logout: post: tags: [Auth] summary: Cerrar sesión e invalidar tokens responses: '200': description: Sesión cerrada exitosamente /auth/setup/status: get: tags: [Auth] summary: Verificar si el sistema requiere configuración inicial security: [] responses: '200': description: Estado de setup content: application/json: schema: type: object properties: requires_setup: type: boolean has_users: type: boolean /auth/setup/register: post: tags: [Auth] summary: Crear primer administrador (solo cuando no hay usuarios) security: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '201': description: Administrador inicial creado content: application/json: schema: $ref: '#/components/schemas/LoginResponse' '409': description: El sistema ya fue configurado # ── Users (Admin) ───────────────────────────────────────────────────────── /admin/users: get: tags: [Users] summary: Listar todos los usuarios del sistema responses: '200': description: Lista de usuarios content: application/json: schema: type: array items: $ref: '#/components/schemas/User' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' post: tags: [Users] summary: Crear nuevo usuario requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '201': description: Usuario creado content: application/json: schema: $ref: '#/components/schemas/User' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /admin/users/{id}: get: tags: [Users] summary: Obtener usuario por ID parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Datos del usuario content: application/json: schema: $ref: '#/components/schemas/User' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' put: tags: [Users] summary: Actualizar datos de un usuario parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '200': description: Usuario actualizado content: application/json: schema: $ref: '#/components/schemas/User' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /admin/users/{id}/reset-password: post: tags: [Users] summary: Resetear contraseña de un usuario parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: type: object required: [new_password] properties: new_password: type: string minLength: 8 responses: '200': description: Contraseña actualizada '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /admin/users/{id}/projects: put: tags: [Users] summary: Asignar proyectos edge al usuario parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: type: object required: [project_ids] properties: project_ids: type: array items: type: string format: uuid responses: '200': description: Proyectos asignados '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /admin/roles: get: tags: [Users] summary: Listar roles disponibles en el sistema responses: '200': description: Lista de roles content: application/json: schema: type: array items: type: string enum: [superadmin, admin, supervisor, operador, anh_reader] '401': $ref: '#/components/responses/Unauthorized' # ── Dashboard ───────────────────────────────────────────────────────────── /dashboard/summary: get: tags: [Dashboard] summary: Resumen del dashboard con estadísticas del proyecto activo responses: '200': description: Datos del dashboard content: application/json: schema: type: object properties: active_assets: type: integer total_alarms: type: integer recent_telemetry: type: array items: type: object compliance_status: type: string '401': $ref: '#/components/responses/Unauthorized' # ── Telemetry ───────────────────────────────────────────────────────────── /telemetry: post: tags: [Telemetry] summary: Enviar datos de telemetría desde campo hacia la plataforma requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TelemetryPayload' responses: '201': description: Telemetría ingresada correctamente '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /telemetry/{asset_id}: get: tags: [Telemetry] summary: Obtener telemetría reciente de un asset parameters: - name: asset_id in: path required: true schema: type: string format: uuid - name: limit in: query schema: type: integer default: 100 responses: '200': description: Lecturas de telemetría recientes content: application/json: schema: type: array items: $ref: '#/components/schemas/TelemetryPayload' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /telemetry/{asset_id}/history: get: tags: [Telemetry] summary: Historial agregado de telemetría en ventanas de 5 minutos (ANH) parameters: - name: asset_id in: path required: true schema: type: string format: uuid - name: from in: query schema: type: string format: date-time - name: to in: query schema: type: string format: date-time responses: '200': description: Historial agregado de telemetría content: application/json: schema: type: array items: type: object properties: bucket: type: string format: date-time variable: type: string avg_value: type: number min_value: type: number max_value: type: number '401': $ref: '#/components/responses/Unauthorized' # ── ANH Reports ─────────────────────────────────────────────────────────── /anh/reports: get: tags: [ANH Reports] summary: Listar historial de reportes ANH generados (Resolución 0651) parameters: - name: project_id in: query schema: type: string format: uuid responses: '200': description: Lista de reportes ANH content: application/json: schema: type: array items: $ref: '#/components/schemas/ANHReport' '401': $ref: '#/components/responses/Unauthorized' /anh/reports/{id}: get: tags: [ANH Reports] summary: Obtener detalle y contenido JSON de un reporte ANH parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Detalle del reporte ANH content: application/json: schema: $ref: '#/components/schemas/ANHReport' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /anh/reports/{id}/download: get: tags: [ANH Reports] summary: Descargar JSON oficial del reporte ANH para validación externa parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Archivo JSON del reporte content: application/json: schema: type: object '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /anh/reports/{id}/hash: get: tags: [ANH Reports] summary: Descargar hash SHA-384 del reporte para auditoría de integridad parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Hash SHA-384 del reporte content: text/plain: schema: type: string example: "384:abc123..." '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' # ── Alerts & Alarms ─────────────────────────────────────────────────────── /alert-rules: get: tags: [Alerts] summary: Listar reglas de alerta del proyecto parameters: - name: project_id in: query required: true schema: type: string format: uuid responses: '200': description: Lista de reglas de alerta content: application/json: schema: type: array items: $ref: '#/components/schemas/AlertRule' '401': $ref: '#/components/responses/Unauthorized' post: tags: [Alerts] summary: Crear nueva regla de alerta requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateAlertRuleRequest' responses: '201': description: Regla creada content: application/json: schema: $ref: '#/components/schemas/AlertRule' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /alert-rules/{id}: put: tags: [Alerts] summary: Actualizar una regla de alerta existente parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateAlertRuleRequest' responses: '200': description: Regla actualizada content: application/json: schema: $ref: '#/components/schemas/AlertRule' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' delete: tags: [Alerts] summary: Eliminar regla de alerta parameters: - name: id in: path required: true schema: type: string format: uuid responses: '204': description: Regla eliminada '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /alert-rules/{id}/toggle: patch: tags: [Alerts] summary: Activar o desactivar una regla de alerta parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Estado de la regla actualizado '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /alarms: get: tags: [Alerts] summary: Listar alarmas activas del proyecto parameters: - name: project_id in: query required: true schema: type: string format: uuid responses: '200': description: Lista de alarmas activas content: application/json: schema: type: array items: $ref: '#/components/schemas/AlarmEvent' '401': $ref: '#/components/responses/Unauthorized' # ── Tanks ───────────────────────────────────────────────────────────────── /tanks: get: tags: [Edge Assets] summary: Listar tanques del proyecto activo parameters: - name: project_id in: query schema: type: string format: uuid responses: '200': description: Lista de tanques content: application/json: schema: type: array items: $ref: '#/components/schemas/EdgeAsset' '401': $ref: '#/components/responses/Unauthorized' # ── Operations ANH 0651 ─────────────────────────────────────────────────── /movements: get: tags: [Operations] summary: Listar movimientos operativos registrados parameters: - name: project_id in: query schema: type: string format: uuid - name: from in: query schema: type: string format: date-time - name: to in: query schema: type: string format: date-time responses: '200': description: Lista de movimientos content: application/json: schema: type: array items: type: object '401': $ref: '#/components/responses/Unauthorized' post: tags: [Operations] summary: Registrar movimiento operativo (ANH 0651) requestBody: required: true content: application/json: schema: type: object required: [project_id, movement_type, volume_bbls, timestamp] properties: project_id: type: string format: uuid movement_type: type: string enum: [production, transfer, injection, flare, loss] volume_bbls: type: number format: double timestamp: type: string format: date-time notes: type: string responses: '201': description: Movimiento registrado '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /downtime: get: tags: [Operations] summary: Listar paros operativos registrados parameters: - name: project_id in: query schema: type: string format: uuid responses: '200': description: Lista de paros operativos content: application/json: schema: type: array items: type: object '401': $ref: '#/components/responses/Unauthorized' post: tags: [Operations] summary: Registrar paro operativo requestBody: required: true content: application/json: schema: type: object required: [project_id, reason, started_at] properties: project_id: type: string format: uuid reason: type: string started_at: type: string format: date-time ended_at: type: string format: date-time nullable: true responses: '201': description: Paro registrado '401': $ref: '#/components/responses/Unauthorized' /well-tests: get: tags: [Operations] summary: Listar pruebas de pozo registradas parameters: - name: project_id in: query schema: type: string format: uuid responses: '200': description: Lista de pruebas de pozo content: application/json: schema: type: array items: type: object '401': $ref: '#/components/responses/Unauthorized' post: tags: [Operations] summary: Registrar prueba de pozo requestBody: required: true content: application/json: schema: type: object required: [project_id, asset_id, test_date] properties: project_id: type: string format: uuid asset_id: type: string format: uuid test_date: type: string format: date-time oil_rate_bopd: type: number water_cut_pct: type: number gor_scf_bbl: type: number responses: '201': description: Prueba de pozo registrada '401': $ref: '#/components/responses/Unauthorized' /lab-results: get: tags: [Operations] summary: Listar resultados de laboratorio parameters: - name: project_id in: query schema: type: string format: uuid responses: '200': description: Lista de resultados de laboratorio content: application/json: schema: type: array items: type: object '401': $ref: '#/components/responses/Unauthorized' post: tags: [Operations] summary: Registrar resultado de análisis de laboratorio requestBody: required: true content: application/json: schema: type: object required: [project_id, sample_date, api_gravity] properties: project_id: type: string format: uuid sample_date: type: string format: date-time api_gravity: type: number bsw_pct: type: number viscosity_cp: type: number responses: '201': description: Resultado registrado '401': $ref: '#/components/responses/Unauthorized' /meters/readings: get: tags: [Operations] summary: Listar lecturas de medidores de flujo parameters: - name: project_id in: query schema: type: string format: uuid responses: '200': description: Lista de lecturas de medidores content: application/json: schema: type: array items: type: object '401': $ref: '#/components/responses/Unauthorized' post: tags: [Operations] summary: Registrar lectura de medidor de flujo requestBody: required: true content: application/json: schema: type: object required: [project_id, meter_id, reading_value, read_at] properties: project_id: type: string format: uuid meter_id: type: string format: uuid reading_value: type: number read_at: type: string format: date-time unit: type: string example: "BBL" responses: '201': description: Lectura registrada '401': $ref: '#/components/responses/Unauthorized' # ── Billing ─────────────────────────────────────────────────────────────── /billing/usage: get: tags: [Billing] summary: Consultar uso actual y costo estimado del proyecto parameters: - name: project_id in: query required: true schema: type: string format: uuid responses: '200': description: Datos de uso y facturación content: application/json: schema: $ref: '#/components/schemas/BillingUsage' '401': $ref: '#/components/responses/Unauthorized' /billing/invoices: get: tags: [Billing] summary: Listar historial de facturas del proyecto parameters: - name: project_id in: query required: true schema: type: string format: uuid responses: '200': description: Lista de facturas content: application/json: schema: type: array items: $ref: '#/components/schemas/Invoice' '401': $ref: '#/components/responses/Unauthorized' /billing/admin/summary: get: tags: [Billing] summary: Resumen de revenue global (solo superadmin) responses: '200': description: Resumen de facturación global content: application/json: schema: type: object properties: total_revenue_usd: type: number active_subscriptions: type: integer mrr_usd: type: number '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' /billing/admin/subscriptions/{project_id}: put: tags: [Billing] summary: Actualizar plan de suscripción de un proyecto parameters: - name: project_id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: type: object required: [plan] properties: plan: type: string enum: [starter, professional, enterprise] responses: '200': description: Suscripción actualizada '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' /billing/invoices/{id}/status: patch: tags: [Billing] summary: Actualizar estado de una factura parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: type: object required: [status] properties: status: type: string enum: [pending, paid, overdue, cancelled] responses: '200': description: Estado actualizado '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /billing/snapshots: post: tags: [Billing] summary: Tomar snapshot manual del uso actual para facturación requestBody: required: true content: application/json: schema: type: object required: [project_id] properties: project_id: type: string format: uuid responses: '201': description: Snapshot registrado '401': $ref: '#/components/responses/Unauthorized' # ── Edge Projects ───────────────────────────────────────────────────────── /edge/projects: get: tags: [Edge Projects] summary: Listar proyectos edge del usuario autenticado responses: '200': description: Lista de proyectos edge content: application/json: schema: type: array items: $ref: '#/components/schemas/EdgeProject' '401': $ref: '#/components/responses/Unauthorized' post: tags: [Edge Projects] summary: Crear nuevo proyecto edge requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateProjectRequest' responses: '201': description: Proyecto creado content: application/json: schema: $ref: '#/components/schemas/EdgeProject' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /edge/projects/{id}: get: tags: [Edge Projects] summary: Obtener detalles de un proyecto edge parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Detalles del proyecto content: application/json: schema: $ref: '#/components/schemas/EdgeProject' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' put: tags: [Edge Projects] summary: Actualizar proyecto edge parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateProjectRequest' responses: '200': description: Proyecto actualizado content: application/json: schema: $ref: '#/components/schemas/EdgeProject' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' delete: tags: [Edge Projects] summary: Eliminar proyecto edge y todos sus recursos parameters: - name: id in: path required: true schema: type: string format: uuid responses: '204': description: Proyecto eliminado '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /edge/projects/{id}/full: get: tags: [Edge Projects] summary: Obtener configuración completa del proyecto (assets, devices, variables) parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Configuración completa del proyecto content: application/json: schema: type: object '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /edge/projects/{id}/installer: get: tags: [Edge Projects] summary: Descargar instalador MSI del agente edge para Windows parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Archivo MSI del instalador content: application/octet-stream: schema: type: string format: binary '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /edge/projects/{id}/deploy: post: tags: [Edge Projects] summary: Desplegar configuración actualizada al agente edge via MQTT parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Despliegue iniciado '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' /edge/projects/{id}/rebuild: post: tags: [Edge Projects] summary: Reconstruir instalador MSI del agente edge parameters: - name: id in: path required: true schema: type: string format: uuid responses: '202': description: Rebuild encolado '401': $ref: '#/components/responses/Unauthorized' /edge/projects/{id}/linux-install: get: tags: [Edge Projects] summary: Obtener script de instalación del agente para Linux parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Script bash de instalación content: text/x-shellscript: schema: type: string '401': $ref: '#/components/responses/Unauthorized' /edge/agent/linux-binary/{arch}: get: tags: [Edge Projects] summary: Descargar binario del agente edge para Linux parameters: - name: arch in: path required: true schema: type: string enum: [x86_64, aarch64] responses: '200': description: Binario compilado del agente content: application/octet-stream: schema: type: string format: binary '401': $ref: '#/components/responses/Unauthorized' /edge/projects/{project_id}/deployments: get: tags: [Edge Projects] summary: Listar historial de deployments del proyecto parameters: - name: project_id in: path required: true schema: type: string format: uuid responses: '200': description: Lista de deployments content: application/json: schema: type: array items: type: object '401': $ref: '#/components/responses/Unauthorized' /edge/projects/{project_id}/build: post: tags: [Edge Projects] summary: Disparar build del instalador del proyecto parameters: - name: project_id in: path required: true schema: type: string format: uuid responses: '202': description: Build encolado '401': $ref: '#/components/responses/Unauthorized' # ── Edge Assets ─────────────────────────────────────────────────────────── /edge/projects/{project_id}/assets: get: tags: [Edge Assets] summary: Listar assets (pozos, tanques) del proyecto parameters: - name: project_id in: path required: true schema: type: string format: uuid responses: '200': description: Lista de assets del proyecto content: application/json: schema: type: array items: $ref: '#/components/schemas/EdgeAsset' '401': $ref: '#/components/responses/Unauthorized' /edge/assets: post: tags: [Edge Assets] summary: Crear nuevo asset en un proyecto requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateAssetRequest' responses: '201': description: Asset creado content: application/json: schema: $ref: '#/components/schemas/EdgeAsset' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /edge/assets/{id}: get: tags: [Edge Assets] summary: Obtener detalles de un asset parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Detalles del asset content: application/json: schema: $ref: '#/components/schemas/EdgeAsset' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' put: tags: [Edge Assets] summary: Actualizar asset parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateAssetRequest' responses: '200': description: Asset actualizado content: application/json: schema: $ref: '#/components/schemas/EdgeAsset' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' delete: tags: [Edge Assets] summary: Eliminar asset y sus dispositivos parameters: - name: id in: path required: true schema: type: string format: uuid responses: '204': description: Asset eliminado '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' # ── Edge Devices ────────────────────────────────────────────────────────── /edge/assets/{asset_id}/devices: get: tags: [Edge Devices] summary: Listar dispositivos de campo del asset (PLCs, RTUs, etc.) parameters: - name: asset_id in: path required: true schema: type: string format: uuid responses: '200': description: Lista de dispositivos content: application/json: schema: type: array items: $ref: '#/components/schemas/EdgeDevice' '401': $ref: '#/components/responses/Unauthorized' post: tags: [Edge Devices] summary: Crear nuevo dispositivo de campo en el asset parameters: - name: asset_id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateDeviceRequest' responses: '201': description: Dispositivo creado content: application/json: schema: $ref: '#/components/schemas/EdgeDevice' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /edge/devices/{id}: put: tags: [Edge Devices] summary: Actualizar configuración de un dispositivo parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateDeviceRequest' responses: '200': description: Dispositivo actualizado content: application/json: schema: $ref: '#/components/schemas/EdgeDevice' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' delete: tags: [Edge Devices] summary: Eliminar dispositivo y sus variables parameters: - name: id in: path required: true schema: type: string format: uuid responses: '204': description: Dispositivo eliminado '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' # ── Edge Variables ──────────────────────────────────────────────────────── /edge/devices/{device_id}/variables: get: tags: [Edge Variables] summary: Listar variables/tags configuradas en el dispositivo parameters: - name: device_id in: path required: true schema: type: string format: uuid responses: '200': description: Lista de variables content: application/json: schema: type: array items: $ref: '#/components/schemas/EdgeVariable' '401': $ref: '#/components/responses/Unauthorized' post: tags: [Edge Variables] summary: Crear variable/tag en el dispositivo parameters: - name: device_id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateVariableRequest' responses: '201': description: Variable creada content: application/json: schema: $ref: '#/components/schemas/EdgeVariable' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' /edge/variables/{id}: put: tags: [Edge Variables] summary: Actualizar variable/tag del dispositivo parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateVariableRequest' responses: '200': description: Variable actualizada content: application/json: schema: $ref: '#/components/schemas/EdgeVariable' '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' delete: tags: [Edge Variables] summary: Eliminar variable/tag del dispositivo parameters: - name: id in: path required: true schema: type: string format: uuid responses: '204': description: Variable eliminada '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' # ── Edge Agents ─────────────────────────────────────────────────────────── /edge/agents/logs: get: tags: [Edge Projects] summary: Consultar logs del agente edge (filtrado por proyecto) parameters: - name: project_id in: query required: true schema: type: string format: uuid - name: level in: query schema: type: string enum: [debug, info, warn, error] - name: limit in: query schema: type: integer default: 100 responses: '200': description: Lista de entradas de log content: application/json: schema: type: array items: type: object properties: id: type: string format: uuid level: type: string category: type: string message: type: string timestamp: type: string format: date-time '401': $ref: '#/components/responses/Unauthorized' post: tags: [Edge Projects] summary: Enviar entrada de log desde el agente edge (uso interno) requestBody: required: true content: application/json: schema: type: object required: [project_id, level, message] properties: project_id: type: string format: uuid level: type: string enum: [debug, info, warn, error] category: type: string message: type: string responses: '201': description: Log registrado '401': $ref: '#/components/responses/Unauthorized' /edge/agents/{project_id}/health: get: tags: [Edge Projects] summary: Obtener estado de salud del agente edge de un proyecto parameters: - name: project_id in: path required: true schema: type: string format: uuid responses: '200': description: Estado de salud del agente content: application/json: schema: type: object properties: status: type: string enum: [online, offline, degraded] last_seen: type: string format: date-time uptime_seconds: type: integer '401': $ref: '#/components/responses/Unauthorized' '404': $ref: '#/components/responses/NotFound' # ── WebSocket ───────────────────────────────────────────────────────────── /ws/telemetry: get: tags: [Telemetry] summary: Stream WebSocket de telemetría en tiempo real description: | Conectar al stream de telemetría en tiempo real vía WebSocket. Los browsers deben pasar el JWT como query param `token` ya que no pueden enviar el header Authorization en conexiones WebSocket. parameters: - name: project_id in: query schema: type: string format: uuid description: Filtrar eventos por proyecto - name: token in: query required: true schema: type: string description: JWT Bearer token para autenticación WebSocket responses: '101': description: Switching Protocols — conexión WebSocket establecida '401': description: Token inválido o ausente # ── System ──────────────────────────────────────────────────────────────── /health: get: tags: [System] summary: Estado de salud detallado del sistema security: [] servers: - url: http://localhost:8000 description: Direct (no /api prefix) responses: '200': description: Estado del sistema content: application/json: schema: $ref: '#/components/schemas/HealthResponse' /metrics: get: tags: [System] summary: Métricas Prometheus (uso interno — no expuesto públicamente) security: [] servers: - url: http://localhost:8000 description: Direct (no /api prefix) responses: '200': description: Métricas en formato Prometheus text content: text/plain: schema: type: string components: responses: Unauthorized: description: Token JWT ausente o inválido content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' Forbidden: description: Permisos insuficientes para esta operación content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' NotFound: description: Recurso no encontrado content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' BadRequest: description: Datos de entrada inválidos content: application/json: schema: $ref: '#/components/schemas/ErrorResponse'