{"openapi":"3.1.0","info":{"title":"API MDM","description":"API REST del sistema **MDM** (*Mobile Device Management*) para gestionar\ndispositivos Android, usuarios, grupos, tokens de enrollment, telemetría y\nubicaciones.\n\n## Autenticación\n\n* **Panel (endpoints privados):** JWT portador emitido por `POST /api/auth/login`.\n  Envíalo en `Authorization: Bearer <token>`.\n* **Dispositivos (endpoints públicos):** se autentican con `token_dispositivo`\n  entregado al hacer enrollment. El check-in lo envía en el cuerpo; la\n  auto-actualización en el header `X-Device-Token`.\n\n## Roles\n\n* `admin`: acceso total. Puede gestionar usuarios, grupos y todos los dispositivos.\n* `usuario`: sólo ve y modifica sus propios dispositivos y los grupos en los que\n  están. No puede crear otros usuarios.\n\n## Formato de errores\n\nTodos los errores siguen el esquema `ErrorRespuesta` con los campos\n`codigo`, `mensaje`, `detalles` y `request_id`. El `request_id` aparece también\nen la cabecera `X-Request-ID` de la respuesta, úsalo al reportar incidencias.\n\n## Rate limiting\n\nLos endpoints sensibles (`/api/auth/login` y el árbol `/api/publico/*`) tienen\nun límite por IP. Si lo excedes, recibirás un `429 Too Many Requests` con el\nesquema `ErrorRespuesta` y `codigo=\"rate_limit\"`.\n","contact":{"name":"MDM Team","url":"https://github.com/rafajordan/mdm"},"license":{"name":"MIT"},"version":"2.1.0"},"paths":{"/":{"get":{"tags":["Sistema"],"summary":"Portada de la API (HTML) o metadatos JSON","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"tags":["Sistema"],"summary":"Health check (incluye ping a la base de datos)","description":"Devuelve `ok` si la API y la base de datos responden. Si la BD está caída, la respuesta será `503` con `estado='degraded'` y `base_datos='error'`.","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EstadoSalud"}}}},"503":{"description":"La API responde pero un subsistema crítico (BD) está caído.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"servicio_no_disponible","mensaje":"La API responde pero la base de datos no está disponible.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}}}}},"/version":{"get":{"tags":["Sistema"],"summary":"Información detallada de versión","operationId":"version_version_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/login":{"post":{"tags":["Autenticación"],"summary":"Login (JWT)","description":"Valida credenciales `email` + `password` y devuelve un token JWT portador.","operationId":"login_api_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Login"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Token"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}}}}},"/api/auth/me":{"get":{"tags":["Autenticación"],"summary":"Perfil del usuario autenticado","description":"Devuelve el perfil del usuario dueño del JWT enviado.","operationId":"obtener_usuario_actual_info_api_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsuarioRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/usuarios/":{"get":{"tags":["Usuarios"],"summary":"Listar usuarios (admin)","description":"Devuelve la lista paginada de usuarios. Sólo disponible para admins.","operationId":"listar_usuarios_api_usuarios__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UsuarioRespuesta"},"title":"Response Listar Usuarios Api Usuarios  Get"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"post":{"tags":["Usuarios"],"summary":"Crear usuario (admin)","description":"Crea un nuevo usuario del panel. Requiere rol `admin`.","operationId":"crear_usuario_api_usuarios__post","security":[{"OAuth2PasswordBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsuarioCrear"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsuarioRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/usuarios/{usuario_id}":{"get":{"tags":["Usuarios"],"summary":"Obtener usuario por ID","description":"Devuelve un usuario por ID. Los no-admin sólo pueden consultar su propio usuario.","operationId":"obtener_usuario_api_usuarios__usuario_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"usuario_id","in":"path","required":true,"schema":{"type":"integer","title":"Usuario Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsuarioRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"put":{"tags":["Usuarios"],"summary":"Actualizar usuario","description":"Actualiza datos del usuario. Los no-admin sólo pueden actualizarse a sí mismos y no pueden cambiar `rol` ni `activo`.","operationId":"actualizar_usuario_api_usuarios__usuario_id__put","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"usuario_id","in":"path","required":true,"schema":{"type":"integer","title":"Usuario Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsuarioActualizar"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsuarioRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"delete":{"tags":["Usuarios"],"summary":"Eliminar usuario (admin)","description":"Elimina un usuario y los dispositivos a su nombre (cascade). No puedes auto-eliminarte.","operationId":"eliminar_usuario_api_usuarios__usuario_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"usuario_id","in":"path","required":true,"schema":{"type":"integer","title":"Usuario Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/grupos/":{"get":{"tags":["Grupos"],"summary":"Listar grupos","description":"Lista los grupos visibles al usuario. Los admins ven todos; los usuarios sólo ven los grupos que contienen al menos uno de sus dispositivos.","operationId":"listar_grupos_api_grupos__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"buscar","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Buscar por nombre o descripción.","title":"Buscar"},"description":"Buscar por nombre o descripción."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GrupoRespuesta"},"title":"Response Listar Grupos Api Grupos  Get"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"post":{"tags":["Grupos"],"summary":"Crear grupo (admin)","description":"Crea un nuevo grupo de dispositivos. Nombre único (case-insensitive).","operationId":"crear_grupo_api_grupos__post","security":[{"OAuth2PasswordBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrupoCrear"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrupoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/grupos/{grupo_id}":{"get":{"tags":["Grupos"],"summary":"Obtener grupo","description":"Devuelve un grupo por ID. Visible sólo si el usuario tiene acceso.","operationId":"obtener_grupo_api_grupos__grupo_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"grupo_id","in":"path","required":true,"schema":{"type":"integer","title":"Grupo Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrupoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"put":{"tags":["Grupos"],"summary":"Actualizar grupo (admin)","description":"Actualiza el nombre o la descripción del grupo.","operationId":"actualizar_grupo_api_grupos__grupo_id__put","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"grupo_id","in":"path","required":true,"schema":{"type":"integer","title":"Grupo Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrupoActualizar"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrupoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"delete":{"tags":["Grupos"],"summary":"Eliminar grupo (admin)","description":"Elimina un grupo. Los dispositivos asociados no se borran, sólo pierden la asociación con este grupo.","operationId":"eliminar_grupo_api_grupos__grupo_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"grupo_id","in":"path","required":true,"schema":{"type":"integer","title":"Grupo Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/grupos/{grupo_id}/detalle":{"get":{"tags":["Grupos"],"summary":"Detalle del grupo con dispositivos visibles al usuario","description":"Como /{grupo_id}, pero incluye el total y la lista de IDs de dispositivos del grupo que son visibles al usuario autenticado.","operationId":"obtener_grupo_detalle_api_grupos__grupo_id__detalle_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"grupo_id","in":"path","required":true,"schema":{"type":"integer","title":"Grupo Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrupoConDispositivos"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/comandos/catalogo":{"get":{"tags":["Comandos"],"summary":"Catálogo de comandos soportados","description":"Lista los tipos de comando definidos por el servidor (misma lista que debe implementar el APK). Cada entrada incluye si requiere payload y un ejemplo.","operationId":"obtener_catalogo_comandos_api_comandos_catalogo_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogoComandosRespuesta"}}}}},"security":[{"OAuth2PasswordBearer":[]}]}},"/api/auditoria":{"get":{"tags":["Auditoría"],"summary":"Listar eventos de auditoría","description":"Devuelve los últimos eventos registrados. Solo administradores.","operationId":"obtener_auditoria_api_auditoria_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditoriaLista"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/tokens-enrollment":{"post":{"tags":["Tokens de Enrollment"],"summary":"Generar token de enrollment","description":"Genera un token para enrollar dispositivos. Los no-admin sólo pueden asignar el token a sí mismos o dejarlo sin asignar.","operationId":"crear_token_api_dispositivos_tokens_enrollment_post","security":[{"OAuth2PasswordBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenEnrollmentCrear"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenEnrollmentGenerado"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"get":{"tags":["Tokens de Enrollment"],"summary":"Listar tokens de enrollment","description":"Listado paginado de tokens de enrollment. Los no-admin sólo ven los tokens que han creado ellos mismos.","operationId":"listar_tokens_api_dispositivos_tokens_enrollment_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"activos","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filtrar por tokens activos.","title":"Activos"},"description":"Filtrar por tokens activos."},{"name":"usados","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filtrar por tokens usados.","title":"Usados"},"description":"Filtrar por tokens usados."},{"name":"usuario_creador_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filtrar por creador (ignorado si no eres admin).","title":"Usuario Creador Id"},"description":"Filtrar por creador (ignorado si no eres admin)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenEnrollmentLista"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/tokens-enrollment/{token_id}":{"get":{"tags":["Tokens de Enrollment"],"summary":"Obtener token de enrollment por ID","description":"Devuelve un token de enrollment con sus relaciones (creador, asignado, grupo).","operationId":"obtener_token_api_dispositivos_tokens_enrollment__token_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"integer","title":"Token Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenEnrollmentConRelaciones"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/tokens-enrollment/{token_id}/desactivar":{"post":{"tags":["Tokens de Enrollment"],"summary":"Desactivar token de enrollment","description":"Marca el token como inactivo, impidiendo su uso para nuevos enrollments.","operationId":"desactivar_token_api_dispositivos_tokens_enrollment__token_id__desactivar_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"integer","title":"Token Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenEnrollmentRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/tokens-enrollment/{token_id}/activar":{"post":{"tags":["Tokens de Enrollment"],"summary":"Reactivar token de enrollment","description":"Reactiva un token desactivado. No se puede reactivar un token ya usado ni uno expirado.","operationId":"activar_token_api_dispositivos_tokens_enrollment__token_id__activar_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"integer","title":"Token Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenEnrollmentRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/":{"get":{"tags":["Dispositivos"],"summary":"Listar dispositivos","description":"Lista paginada de dispositivos. Los admins ven todos; los no-admin sólo ven los suyos (el filtro `usuario_id` se ignora para éstos).","operationId":"listar_dispositivos_api_dispositivos__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":100,"title":"Limit"}},{"name":"usuario_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filtra por usuario propietario (sólo admin).","title":"Usuario Id"},"description":"Filtra por usuario propietario (sólo admin)."},{"name":"estado","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/EstadoDispositivo"},{"type":"null"}],"title":"Estado"}},{"name":"en_linea","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filtra por conectividad según umbral offline de la política efectiva.","title":"En Linea"},"description":"Filtra por conectividad según umbral offline de la política efectiva."},{"name":"grupo_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Filtra dispositivos del grupo.","title":"Grupo Id"},"description":"Filtra dispositivos del grupo."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DispositivoRespuesta"},"title":"Response Listar Dispositivos Api Dispositivos  Get"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"post":{"tags":["Dispositivos"],"summary":"Crear dispositivo desde el panel","description":"Crea un dispositivo manualmente (sin enrollment). Los no-admin sólo pueden crear dispositivos a su nombre.","operationId":"crear_dispositivo_api_dispositivos__post","security":[{"OAuth2PasswordBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoCrear"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/mapa/puntos":{"get":{"tags":["Dispositivos"],"summary":"Puntos del mapa (última ubicación)","description":"Lista dispositivos visibles para el usuario con su última ubicación GPS en una sola consulta (panel mapa).","operationId":"listar_puntos_mapa_api_dispositivos_mapa_puntos_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":250,"minimum":1,"default":250,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DispositivoMapaPunto"},"title":"Response Listar Puntos Mapa Api Dispositivos Mapa Puntos Get"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}":{"get":{"tags":["Dispositivos"],"summary":"Obtener dispositivo","description":"Devuelve un dispositivo por ID. El usuario debe tener permiso de acceso.","operationId":"obtener_dispositivo_api_dispositivos__dispositivo_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"put":{"tags":["Dispositivos"],"summary":"Actualizar dispositivo","description":"Actualiza datos del dispositivo. Sólo los admins pueden reasignarlo a otro usuario (`usuario_id`).","operationId":"actualizar_dispositivo_api_dispositivos__dispositivo_id__put","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoActualizar"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"delete":{"tags":["Dispositivos"],"summary":"Eliminar dispositivo (admin)","description":"Borra un dispositivo y todos sus datos asociados (telemetría, ubicaciones, apps). Operación irreversible.","operationId":"eliminar_dispositivo_api_dispositivos__dispositivo_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}}],"responses":{"204":{"description":"Successful Response"},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/detalle":{"get":{"tags":["Dispositivos"],"summary":"Detalle enriquecido del dispositivo","description":"Devuelve el dispositivo con su usuario, grupos, última telemetría, última ubicación, apps instaladas (últimas `limite_apps`) y totales.","operationId":"obtener_dispositivo_detalle_api_dispositivos__dispositivo_id__detalle_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}},{"name":"limite_apps","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limite Apps"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoDetalle"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/telemetria":{"get":{"tags":["Dispositivos"],"summary":"Historial de telemetría","description":"Historial paginado de muestras de telemetría. Soporta filtrado por rango de fechas (ISO 8601, UTC).","operationId":"obtener_telemetria_dispositivo_api_dispositivos__dispositivo_id__telemetria_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"desde","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Fecha inicial (ISO 8601, UTC).","title":"Desde"},"description":"Fecha inicial (ISO 8601, UTC)."},{"name":"hasta","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Fecha final (ISO 8601, UTC).","title":"Hasta"},"description":"Fecha final (ISO 8601, UTC)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TelemetriaRespuesta"},"title":"Response Obtener Telemetria Dispositivo Api Dispositivos  Dispositivo Id  Telemetria Get"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/ubicaciones":{"get":{"tags":["Dispositivos"],"summary":"Historial de ubicaciones","description":"Historial paginado de ubicaciones del dispositivo con filtrado por fecha.","operationId":"obtener_ubicaciones_dispositivo_api_dispositivos__dispositivo_id__ubicaciones_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}},{"name":"desde","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Desde"}},{"name":"hasta","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Hasta"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/UbicacionRespuesta"},"title":"Response Obtener Ubicaciones Dispositivo Api Dispositivos  Dispositivo Id  Ubicaciones Get"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/apps":{"get":{"tags":["Dispositivos"],"summary":"Apps instaladas en el dispositivo","description":"Listado paginado de apps detectadas, opcionalmente filtrado por fecha de detección.","operationId":"obtener_apps_dispositivo_api_dispositivos__dispositivo_id__apps_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":500,"title":"Limit"}},{"name":"desde","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Desde"}},{"name":"hasta","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Hasta"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AppInstaladaRespuesta"},"title":"Response Obtener Apps Dispositivo Api Dispositivos  Dispositivo Id  Apps Get"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/desenrollar":{"post":{"tags":["Dispositivos"],"summary":"Desenrollar dispositivo","description":"Marca el dispositivo como `desenrollado`. Bloquea sus futuros check-ins.","operationId":"desenrollar_dispositivo_api_dispositivos__dispositivo_id__desenrollar_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/desvincular":{"post":{"tags":["Dispositivos"],"summary":"Desvincular dispositivo","description":"Alias de desenrollar: marca el dispositivo como `desenrollado` e invalida su `token_dispositivo`.","operationId":"desvincular_dispositivo_api_dispositivos__dispositivo_id__desvincular_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/reactivar":{"post":{"tags":["Dispositivos"],"summary":"Reactivar dispositivo desenrollado","description":"Restaura el dispositivo al estado `enrollado`. Debe estar `desenrollado`.","operationId":"reactivar_dispositivo_api_dispositivos__dispositivo_id__reactivar_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/regenerar-token":{"post":{"tags":["Dispositivos"],"summary":"Regenerar el token del dispositivo (admin)","description":"Genera un nuevo `token_dispositivo` e invalida el anterior. El valor se devuelve **una única vez** en esta respuesta; no vuelve a aparecer en ningún otro endpoint.","operationId":"regenerar_token_dispositivo_api_dispositivos__dispositivo_id__regenerar_token_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoRegeneradoToken"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/grupos/{grupo_id}":{"post":{"tags":["Dispositivos"],"summary":"Asignar grupo al dispositivo","description":"Añade el dispositivo al grupo indicado. Idempotente.","operationId":"asignar_grupo_dispositivo_api_dispositivos__dispositivo_id__grupos__grupo_id__post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}},{"name":"grupo_id","in":"path","required":true,"schema":{"type":"integer","title":"Grupo Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"delete":{"tags":["Dispositivos"],"summary":"Quitar grupo del dispositivo","description":"Elimina la asociación entre el dispositivo y el grupo. Idempotente.","operationId":"remover_grupo_dispositivo_api_dispositivos__dispositivo_id__grupos__grupo_id__delete","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}},{"name":"grupo_id","in":"path","required":true,"schema":{"type":"integer","title":"Grupo Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/comandos":{"get":{"tags":["Dispositivos"],"summary":"Listar comandos del dispositivo","description":"Historial de la cola de comandos, más reciente primero.","operationId":"listar_comandos_dispositivo_api_dispositivos__dispositivo_id__comandos_get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"estado","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/EstadoComando"},{"type":"null"}],"description":"Filtra por estado.","title":"Estado"},"description":"Filtra por estado."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ComandoDispositivoRespuesta"},"title":"Response Listar Comandos Dispositivo Api Dispositivos  Dispositivo Id  Comandos Get"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}},"post":{"tags":["Dispositivos"],"summary":"Encolar comando para el dispositivo","description":"Crea un comando en estado `pendiente`. El APK lo recogerá con `GET /api/publico/comandos/pendientes` (header `X-Device-Token`).","operationId":"crear_comando_dispositivo_api_dispositivos__dispositivo_id__comandos_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComandoDispositivoCrear"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComandoDispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/comandos/{comando_id}":{"get":{"tags":["Dispositivos"],"summary":"Obtener un comando","operationId":"obtener_comando_dispositivo_api_dispositivos__dispositivo_id__comandos__comando_id__get","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}},{"name":"comando_id","in":"path","required":true,"schema":{"type":"integer","title":"Comando Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComandoDispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/dispositivos/{dispositivo_id}/comandos/{comando_id}/cancelar":{"post":{"tags":["Dispositivos"],"summary":"Cancelar comando pendiente","description":"Sólo tiene efecto si el comando sigue en estado `pendiente`.","operationId":"cancelar_comando_dispositivo_api_dispositivos__dispositivo_id__comandos__comando_id__cancelar_post","security":[{"OAuth2PasswordBearer":[]}],"parameters":[{"name":"dispositivo_id","in":"path","required":true,"schema":{"type":"integer","title":"Dispositivo Id"}},{"name":"comando_id","in":"path","required":true,"schema":{"type":"integer","title":"Comando Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComandoDispositivoRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/publico/checkin":{"post":{"tags":["Público"],"summary":"Check-in periódico del dispositivo","description":"Latido periódico del dispositivo. Actualiza estado, telemetría, ubicación y apps detectadas. Requiere `token_dispositivo` (entregado en el enrollment). El modo legacy por `identificador_unico` sólo está disponible si el servidor tiene `LEGACY_CHECKIN_IDENTIFICADOR=true`.\n\nLimitado a 60 peticiones por minuto y dirección IP.","operationId":"checkin_dispositivo_api_publico_checkin_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckinDispositivo"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckinResponse"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}}}}},"/api/publico/enroll":{"post":{"tags":["Público"],"summary":"Enrollment de un dispositivo","description":"Registra (o re-registra tras desenrollo) un dispositivo y entrega su `token_dispositivo`. El servidor puede exigir un `token_enrollment` válido (`ENROLLMENT_REQUIERE_TOKEN=true`).\n\nLimitado a 10 peticiones por minuto y dirección IP.","operationId":"enroll_dispositivo_api_publico_enroll_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollmentDispositivo"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnrollmentRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorRespuesta"},"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"}}}}}}},"/api/publico/tokens-enrollment/{token}/info":{"get":{"tags":["Público"],"summary":"Información pública de un token de enrollment","description":"Devuelve validez y metadata del token (usuario y grupo a asignar). Si el token no existe o no es válido, el campo `valido=false` y el campo `error` indica la razón.\n\nLimitado a 20 peticiones por minuto.","operationId":"obtener_info_token_enrollment_api_publico_tokens_enrollment__token__info_get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenEnrollmentInfo"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/publico/comandos/pendientes":{"get":{"tags":["Público"],"summary":"Comandos pendientes del dispositivo (consume y marca entregados al cliente)","description":"Devuelve los comandos en estado **`pendiente`**, hasta 50 ordenados por `id`, y los pasa a **`enviado`** en la misma transacción (cola tipo *consume*: entrega única hasta resultado).\n\n**Cabecera:** `X-Device-Token` (token persistente del dispositivo).\n\nCada ítem tiene `id`, `tipo` en MAYÚSCULAS, `payload` (objeto o `null` si el tipo no exige datos) y `fecha_creacion`. El cliente ejecuta en orden y notifica cada uno con `POST /api/publico/comandos/{id}/resultado`.\n\nTipos **recomendados** APK **TeamMDM** (`com.rafajordan.mdm`) en rutas públicas típicas: **PING**, **PEDIR_INFO**, **MOSTRAR_MENSAJE**, **ABRIR_ENLACE_NAVEGADOR**. En Android 13+, **MOSTRAR_MENSAJE** requiere que el usuario haya concedido notificaciones a la app.\n\nSi **`COMANDO_REENCOLAR_ENVIADO_MINUTOS`** > `0`, en cada llamada los comandos en `enviado` sin resultado más antiguos que ese umbral vuelven a `pendiente` para reintentar la entrega al APK (valor `0` desactiva la política).","operationId":"listar_comandos_pendientes_api_publico_comandos_pendientes_get","parameters":[{"name":"X-Device-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Device-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ComandoPendienteDispositivo"},"title":"Response Listar Comandos Pendientes Api Publico Comandos Pendientes Get"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/publico/comandos/{comando_id}/resultado":{"post":{"tags":["Público"],"summary":"Informa si el comando se completó o falló en el dispositivo","description":"**Cabecera obligatoria:** `X-Device-Token`.\n\n**Cuerpo JSON:**\n- **`estado`:** `completado` si la acción aplicó bien; **`error`** si hubo fallo en el cliente.\n- **`resultado`:** objeto JSON opcional cuando `completado` (puede ir vacío o omitirse).\n- **`mensaje_error`:** obligatorio cuando `estado` es **`error`**; el APK puede usar códigos estables (p. ej. `permiso_denegado_post_notifications_android13`, `payload_mensaje_o_texto_requerido`, …).\n\nRespuesta **409 Conflict** si el comando ya no admite resultado (ya informado u otro estado); el cliente idempotente debe aceptarlo.\n\nEjemplos típicos *TeamMDM*: ver `GET /api/comandos/catalogo` y **`COMANDOS_REMOTOS_TEAMMDM.md`** en el repositorio del backend.","operationId":"informar_resultado_comando_api_publico_comandos__comando_id__resultado_post","parameters":[{"name":"comando_id","in":"path","required":true,"schema":{"type":"integer","title":"Comando Id"}},{"name":"X-Device-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Device-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComandoResultadoEntrada"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ComandoResultadoRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"404":{"description":"Recurso no encontrado.","content":{"application/json":{"example":{"codigo":"no_encontrado","mensaje":"Recurso no encontrado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"409":{"description":"Conflicto (p.ej. recurso duplicado).","content":{"application/json":{"example":{"codigo":"conflicto","mensaje":"El recurso ya existe.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/publico/actualizar":{"put":{"tags":["Público"],"summary":"El dispositivo actualiza su propia información","description":"Permite al dispositivo actualizar un subconjunto seguro de sus datos (nombre, modelo, versión Android, API level). Requiere el header `X-Device-Token`.\n\nLimitado a 20 peticiones por minuto.","operationId":"actualizar_dispositivo_propio_api_publico_actualizar_put","parameters":[{"name":"X-Device-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Device-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DispositivoActualizarPublico"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActualizacionRespuesta"}}}},"400":{"description":"Petición inválida.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"Datos de entrada no válidos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"422":{"description":"La petición no cumple el esquema esperado.","content":{"application/json":{"example":{"codigo":"validacion","mensaje":"La petición no cumple el esquema esperado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718","detalles":[{"ubicacion":["body","email"],"mensaje":"value is not a valid email address","tipo":"value_error"}]},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"429":{"description":"Se ha excedido el límite de peticiones.","content":{"application/json":{"example":{"codigo":"rate_limit","mensaje":"Demasiadas peticiones. Intenta de nuevo en unos segundos.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"401":{"description":"Falta autenticación o es inválida.","content":{"application/json":{"example":{"codigo":"no_autorizado","mensaje":"No autenticado.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}},"403":{"description":"El usuario no tiene permisos suficientes.","content":{"application/json":{"example":{"codigo":"permiso_denegado","mensaje":"No tienes permisos para realizar esta operación.","request_id":"3f9a1e8b6c4d4e7aa1b2c3d4e5f60718"},"schema":{"$ref":"#/components/schemas/ErrorRespuesta"}}}}}}},"/api/publico/info":{"get":{"tags":["Público"],"summary":"Información pública del servidor MDM","description":"Devuelve versión y política básica del servidor (intervalo de check-in, política de enrollment).","operationId":"obtener_informacion_servidor_api_publico_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InformacionServidor"}}}}}}}},"components":{"schemas":{"ActualizacionRespuesta":{"properties":{"mensaje":{"type":"string","title":"Mensaje"},"dispositivo_id":{"type":"integer","title":"Dispositivo Id"},"nombre":{"type":"string","title":"Nombre"},"modelo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modelo"},"version_android":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Android"},"nivel_api_android":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Nivel Api Android"}},"type":"object","required":["mensaje","dispositivo_id","nombre"],"title":"ActualizacionRespuesta","description":"Respuesta al endpoint público de auto-actualización."},"AppInstaladaCheckin":{"properties":{"nombre_paquete":{"type":"string","maxLength":255,"minLength":1,"title":"Nombre Paquete"},"nombre_aplicacion":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Nombre Aplicacion"},"version":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Version"},"version_codigo":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version Codigo"},"es_sistema":{"type":"boolean","title":"Es Sistema","default":false}},"type":"object","required":["nombre_paquete"],"title":"AppInstaladaCheckin","description":"Entrada de app instalada en el check-in."},"AppInstaladaRespuesta":{"properties":{"id":{"type":"integer","title":"Id"},"nombre_paquete":{"type":"string","title":"Nombre Paquete"},"nombre_aplicacion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nombre Aplicacion"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"version_codigo":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version Codigo"},"es_sistema":{"type":"boolean","title":"Es Sistema"},"fecha_instalacion":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Instalacion"},"fecha_actualizacion":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Actualizacion"},"fecha_deteccion":{"type":"string","format":"date-time","title":"Fecha Deteccion"}},"type":"object","required":["id","nombre_paquete","es_sistema","fecha_deteccion"],"title":"AppInstaladaRespuesta"},"AuditoriaLista":{"properties":{"total":{"type":"integer","title":"Total"},"eventos":{"items":{"$ref":"#/components/schemas/AuditoriaRespuesta"},"type":"array","title":"Eventos"}},"type":"object","required":["total"],"title":"AuditoriaLista"},"AuditoriaRespuesta":{"properties":{"id":{"type":"integer","title":"Id"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"actor_user_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Actor User Id"},"accion":{"type":"string","title":"Accion"},"entidad":{"type":"string","title":"Entidad"},"entidad_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Entidad Id"},"metadata_json":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Metadata Json"},"ip":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip"}},"type":"object","required":["id","timestamp","accion","entidad"],"title":"AuditoriaRespuesta"},"CatalogoComandosRespuesta":{"properties":{"comandos":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Comandos"}},"type":"object","required":["comandos"],"title":"CatalogoComandosRespuesta"},"CheckinDispositivo":{"properties":{"identificador_unico":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Identificador Unico"},"token_dispositivo":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Token Dispositivo"},"modelo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modelo"},"fabricante":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fabricante"},"version_android":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Android"},"imei":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Imei"},"numero_serie":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Numero Serie"},"numero_telefono":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Numero Telefono"},"nivel_api_android":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Nivel Api Android"},"bateria_porcentaje":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Bateria Porcentaje"},"temperatura":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Temperatura"},"cpu_uso_porcentaje":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Cpu Uso Porcentaje"},"memoria_disponible_mb":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Memoria Disponible Mb"},"almacenamiento_disponible_mb":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Almacenamiento Disponible Mb"},"almacenamiento_total_mb":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Almacenamiento Total Mb"},"latitud":{"anyOf":[{"type":"number","maximum":90.0,"minimum":-90.0},{"type":"null"}],"title":"Latitud"},"longitud":{"anyOf":[{"type":"number","maximum":180.0,"minimum":-180.0},{"type":"null"}],"title":"Longitud"},"precision":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Precision"},"apps_instaladas":{"anyOf":[{"items":{"$ref":"#/components/schemas/AppInstaladaCheckin"},"type":"array"},{"type":"null"}],"title":"Apps Instaladas"},"version_app_mdm":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Version App Mdm"}},"type":"object","title":"CheckinDispositivo","description":"Payload de check-in enviado por la app Android.","example":{"almacenamiento_disponible_mb":44500.0,"almacenamiento_total_mb":64000.0,"apps_instaladas":[{"es_sistema":false,"nombre_aplicacion":"WhatsApp","nombre_paquete":"com.whatsapp","version":"2.25.1","version_codigo":250100}],"bateria_porcentaje":87.0,"cpu_uso_porcentaje":11.2,"latitud":40.41678,"longitud":-3.70379,"memoria_disponible_mb":2048.0,"nivel_api_android":33,"precision":12.5,"temperatura":29.4,"token_dispositivo":"b73f88c4d5e64a1f9a21c3b7e0a1f3e2","version_android":"13"}},"CheckinResponse":{"properties":{"mensaje":{"type":"string","title":"Mensaje"},"dispositivo_id":{"type":"integer","title":"Dispositivo Id"},"estado":{"type":"string","title":"Estado"},"intervalo_checkin_minutos":{"type":"integer","maximum":1440.0,"minimum":1.0,"title":"Intervalo Checkin Minutos"},"politica_efectiva":{"$ref":"#/components/schemas/PoliticaEfectiva"}},"type":"object","required":["mensaje","dispositivo_id","estado","intervalo_checkin_minutos","politica_efectiva"],"title":"CheckinResponse","description":"Respuesta al check-in.","example":{"dispositivo_id":42,"estado":"activo","intervalo_checkin_minutos":15,"mensaje":"Check-in exitoso"}},"ComandoDispositivoCrear":{"properties":{"tipo":{"type":"string","title":"Tipo","description":"Identificador en MAYÚSCULAS definido por el servidor. Catálogo: `GET /api/comandos/catalogo`. Tipos **recomendados** APK cliente **TeamMDM** (`com.rafajordan.mdm`): **PING**, **PEDIR_INFO**, **MOSTRAR_MENSAJE**, **ABRIR_ENLACE_NAVEGADOR** (y alias **ABRIR_ENLACE**). Otros tipos pueden depender de versión OEM / Android."},"payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload","description":"Obligatorio para comandos marcados **`requiere_payload`** en catálogo: **MOSTRAR_MENSAJE** (al menos uno de mensaje/texto/cuerpo/body) y **ABRIR_ENLACE_NAVEGADOR**/**ABRIR_ENLACE** (url/enlace/link con http/https y host). Los tipos sin payload (**PING**, **PEDIR_INFO**) deben enviar `null` u omitir el campo."}},"type":"object","required":["tipo"],"title":"ComandoDispositivoCrear"},"ComandoDispositivoRespuesta":{"properties":{"id":{"type":"integer","title":"Id"},"dispositivo_id":{"type":"integer","title":"Dispositivo Id"},"tipo":{"type":"string","title":"Tipo"},"payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload"},"estado":{"type":"string","title":"Estado"},"resultado":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Resultado"},"mensaje_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mensaje Error"},"creado_por_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Creado Por Id"},"fecha_creacion":{"type":"string","format":"date-time","title":"Fecha Creacion"},"fecha_entrega":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Entrega"},"fecha_resultado":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Resultado"}},"type":"object","required":["id","dispositivo_id","tipo","estado","fecha_creacion"],"title":"ComandoDispositivoRespuesta"},"ComandoPendienteDispositivo":{"properties":{"id":{"type":"integer","title":"Id"},"tipo":{"type":"string","title":"Tipo"},"payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload"},"fecha_creacion":{"type":"string","format":"date-time","title":"Fecha Creacion"}},"type":"object","required":["id","tipo","fecha_creacion"],"title":"ComandoPendienteDispositivo","description":"Comando listo para ejecutar en el APK (tras marcarlo como enviado)."},"ComandoResultadoEntrada":{"properties":{"estado":{"type":"string","enum":["completado","error"],"title":"Estado"},"resultado":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Resultado","description":"Información opcional cuando `estado=completado` (p. ej. telemetría en **PEDIR_INFO**, objeto resumido en **PING**, `notificacion_mostrada` tras **MOSTRAR_MENSAJE**, `url` tras **ABRIR_ENLACE_NAVEGADOR**)."},"mensaje_error":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Mensaje Error","description":"Obligatorio si **`estado=error`**; códigos o mensajes estables esperados por el panel/soporte (contrato cliente **TeamMDM** para fallos reproducibles)."}},"type":"object","required":["estado"],"title":"ComandoResultadoEntrada"},"ComandoResultadoRespuesta":{"properties":{"id":{"type":"integer","title":"Id"},"estado":{"type":"string","title":"Estado"},"mensaje":{"type":"string","title":"Mensaje","default":"Resultado registrado"}},"type":"object","required":["id","estado"],"title":"ComandoResultadoRespuesta"},"DispositivoActualizar":{"properties":{"nombre":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Nombre"},"modelo":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Modelo"},"fabricante":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Fabricante"},"version_android":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Version Android"},"imei":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Imei"},"numero_serie":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Numero Serie"},"numero_telefono":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Numero Telefono"},"nivel_api_android":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Nivel Api Android"},"estado":{"anyOf":[{"$ref":"#/components/schemas/EstadoDispositivo"},{"type":"null"}]},"usuario_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usuario Id"},"intervalo_checkin_minutos":{"anyOf":[{"type":"integer","maximum":1440.0,"minimum":1.0},{"type":"null"}],"title":"Intervalo Checkin Minutos","description":"Intervalo de check-in personalizado (1-1440). None = usar default del servidor."}},"type":"object","title":"DispositivoActualizar","description":"Entrada para actualizar un dispositivo desde el panel."},"DispositivoActualizarPublico":{"properties":{"nombre":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Nombre"},"modelo":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Modelo"},"version_android":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Version Android"},"nivel_api_android":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Nivel Api Android"}},"type":"object","title":"DispositivoActualizarPublico","description":"Campos que un dispositivo puede actualizar por sí mismo vía /api/publico/actualizar."},"DispositivoCrear":{"properties":{"identificador_unico":{"type":"string","maxLength":255,"minLength":1,"title":"Identificador Unico","description":"UUID único del dispositivo."},"nombre":{"type":"string","maxLength":255,"minLength":1,"title":"Nombre"},"modelo":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Modelo"},"fabricante":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Fabricante"},"version_android":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Version Android"},"imei":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Imei"},"numero_serie":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Numero Serie"},"numero_telefono":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Numero Telefono"},"nivel_api_android":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Nivel Api Android","description":"API level de Android."},"usuario_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usuario Id","description":"Usuario dueño del dispositivo (obligatorio para admin)."},"estado":{"$ref":"#/components/schemas/EstadoDispositivo","default":"enrollado"}},"type":"object","required":["identificador_unico","nombre"],"title":"DispositivoCrear","description":"Entrada para crear un dispositivo desde el panel."},"DispositivoDetalle":{"properties":{"identificador_unico":{"type":"string","maxLength":255,"minLength":1,"title":"Identificador Unico","description":"UUID único del dispositivo."},"nombre":{"type":"string","maxLength":255,"minLength":1,"title":"Nombre"},"modelo":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Modelo"},"fabricante":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Fabricante"},"version_android":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Version Android"},"imei":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Imei"},"numero_serie":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Numero Serie"},"numero_telefono":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Numero Telefono"},"nivel_api_android":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Nivel Api Android","description":"API level de Android."},"id":{"type":"integer","title":"Id"},"estado":{"$ref":"#/components/schemas/EstadoDispositivo"},"usuario_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usuario Id"},"intervalo_checkin_minutos":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Intervalo Checkin Minutos"},"fecha_ultimo_checkin":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Ultimo Checkin"},"version_app_mdm":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Version App Mdm"},"en_linea":{"type":"boolean","title":"En Linea","default":false},"fecha_creacion":{"type":"string","format":"date-time","title":"Fecha Creacion"},"fecha_actualizacion":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Actualizacion"},"usuario":{"anyOf":[{"$ref":"#/components/schemas/UsuarioBasicoRespuesta"},{"type":"null"}]},"grupos":{"items":{"$ref":"#/components/schemas/GrupoBasicoDispositivo"},"type":"array","title":"Grupos","default":[]},"politica_efectiva":{"anyOf":[{"$ref":"#/components/schemas/PoliticaEfectiva"},{"type":"null"}]},"ultima_telemetria":{"anyOf":[{"$ref":"#/components/schemas/TelemetriaRespuesta"},{"type":"null"}]},"ultima_ubicacion":{"anyOf":[{"$ref":"#/components/schemas/UbicacionRespuesta"},{"type":"null"}]},"apps_instaladas":{"items":{"$ref":"#/components/schemas/AppInstaladaRespuesta"},"type":"array","title":"Apps Instaladas","default":[]},"total_telemetria":{"type":"integer","title":"Total Telemetria","default":0},"total_ubicaciones":{"type":"integer","title":"Total Ubicaciones","default":0},"total_apps":{"type":"integer","title":"Total Apps","default":0}},"type":"object","required":["identificador_unico","nombre","id","estado","fecha_creacion"],"title":"DispositivoDetalle","description":"Representación detallada del dispositivo con sus recursos asociados.","example":{"estado":"activo","fabricante":"Samsung","fecha_actualizacion":"2026-04-16T09:45:12Z","fecha_creacion":"2026-03-01T11:00:00Z","fecha_ultimo_checkin":"2026-04-16T09:45:12Z","id":42,"identificador_unico":"a3f0c2d8-1b7e-4a90-9a2d-11c8e45d1234","imei":"356938035643809","intervalo_checkin_minutos":15,"modelo":"Galaxy Tab A7","nivel_api_android":33,"nombre":"Sucursal Norte - Tablet 01","numero_serie":"R52N30XYZAB","usuario_id":5,"version_android":"13"}},"DispositivoMapaPunto":{"properties":{"id":{"type":"integer","title":"Id"},"nombre":{"type":"string","title":"Nombre"},"estado":{"$ref":"#/components/schemas/EstadoDispositivo"},"modelo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modelo"},"fabricante":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fabricante"},"latitud":{"type":"number","title":"Latitud"},"longitud":{"type":"number","title":"Longitud"},"precision":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Precision"},"timestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Timestamp"},"usuario_nombre":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Usuario Nombre"},"usuario_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usuario Id"}},"type":"object","required":["id","nombre","estado","latitud","longitud"],"title":"DispositivoMapaPunto","description":"Punto ligero para el mapa del panel (última ubicación conocida)."},"DispositivoRegeneradoToken":{"properties":{"dispositivo_id":{"type":"integer","title":"Dispositivo Id"},"token_dispositivo":{"type":"string","title":"Token Dispositivo","description":"Nuevo token del dispositivo. Se muestra SÓLO en esta respuesta."}},"type":"object","required":["dispositivo_id","token_dispositivo"],"title":"DispositivoRegeneradoToken","description":"Respuesta del endpoint admin de regeneración de token."},"DispositivoRespuesta":{"properties":{"identificador_unico":{"type":"string","maxLength":255,"minLength":1,"title":"Identificador Unico","description":"UUID único del dispositivo."},"nombre":{"type":"string","maxLength":255,"minLength":1,"title":"Nombre"},"modelo":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Modelo"},"fabricante":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Fabricante"},"version_android":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Version Android"},"imei":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Imei"},"numero_serie":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Numero Serie"},"numero_telefono":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Numero Telefono"},"nivel_api_android":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Nivel Api Android","description":"API level de Android."},"id":{"type":"integer","title":"Id"},"estado":{"$ref":"#/components/schemas/EstadoDispositivo"},"usuario_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usuario Id"},"intervalo_checkin_minutos":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Intervalo Checkin Minutos"},"fecha_ultimo_checkin":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Ultimo Checkin"},"version_app_mdm":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Version App Mdm"},"en_linea":{"type":"boolean","title":"En Linea","default":false},"fecha_creacion":{"type":"string","format":"date-time","title":"Fecha Creacion"},"fecha_actualizacion":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Actualizacion"}},"type":"object","required":["identificador_unico","nombre","id","estado","fecha_creacion"],"title":"DispositivoRespuesta","description":"Representación pública del dispositivo (sin token_dispositivo).","example":{"estado":"activo","fabricante":"Samsung","fecha_actualizacion":"2026-04-16T09:45:12Z","fecha_creacion":"2026-03-01T11:00:00Z","fecha_ultimo_checkin":"2026-04-16T09:45:12Z","id":42,"identificador_unico":"a3f0c2d8-1b7e-4a90-9a2d-11c8e45d1234","imei":"356938035643809","intervalo_checkin_minutos":15,"modelo":"Galaxy Tab A7","nivel_api_android":33,"nombre":"Sucursal Norte - Tablet 01","numero_serie":"R52N30XYZAB","usuario_id":5,"version_android":"13"}},"EnrollmentDispositivo":{"properties":{"identificador_unico":{"type":"string","minLength":1,"title":"Identificador Unico"},"nombre":{"type":"string","minLength":1,"title":"Nombre"},"modelo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Modelo"},"fabricante":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fabricante"},"version_android":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Android"},"imei":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Imei"},"numero_serie":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Numero Serie"},"numero_telefono":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Numero Telefono"},"nivel_api_android":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Nivel Api Android"},"token_enrollment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Enrollment","description":"Token de enrollment generado desde el panel. Obligatorio si el servidor lo exige."}},"type":"object","required":["identificador_unico","nombre"],"title":"EnrollmentDispositivo","description":"Payload de enrollment enviado por la app Android.","example":{"fabricante":"Samsung","identificador_unico":"a3f0c2d8-1b7e-4a90-9a2d-11c8e45d1234","imei":"356938035643809","modelo":"Galaxy Tab A7","nivel_api_android":33,"nombre":"Tablet 01","numero_serie":"R52N30XYZAB","token_enrollment":"enr_6f5d8c7b4a3e2110aabb","version_android":"13"}},"EnrollmentRespuesta":{"properties":{"mensaje":{"type":"string","title":"Mensaje"},"dispositivo_id":{"type":"integer","title":"Dispositivo Id"},"token_dispositivo":{"type":"string","title":"Token Dispositivo","description":"Token único del dispositivo. Debe guardarse y usarse en todos los check-ins posteriores."},"estado":{"type":"string","title":"Estado"},"re_registro":{"type":"boolean","title":"Re Registro","description":"True si se re-registró un dispositivo desenrollado previamente."}},"type":"object","required":["mensaje","dispositivo_id","token_dispositivo","estado","re_registro"],"title":"EnrollmentRespuesta","description":"Respuesta del endpoint de enrollment.","example":{"dispositivo_id":42,"estado":"enrollado","mensaje":"Dispositivo registrado exitosamente","re_registro":false,"token_dispositivo":"b73f88c4d5e64a1f9a21c3b7e0a1f3e2"}},"ErrorRespuesta":{"properties":{"codigo":{"type":"string","title":"Codigo","description":"Código corto del error (p.ej. 'no_encontrado')."},"mensaje":{"type":"string","title":"Mensaje","description":"Mensaje legible para mostrar al usuario."},"detalles":{"anyOf":[{},{"type":"null"}],"title":"Detalles","description":"Información adicional (p. ej. lista de errores de validación)."},"request_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Request Id","description":"Identificador único de la petición (útil para soporte)."}},"type":"object","required":["codigo","mensaje"],"title":"ErrorRespuesta","description":"Formato único para todas las respuestas de error de la API."},"EstadoComando":{"type":"string","enum":["pendiente","enviado","completado","error","cancelado"],"title":"EstadoComando"},"EstadoDispositivo":{"type":"string","enum":["enrollado","activo","inactivo","desenrollado"],"title":"EstadoDispositivo","description":"Estados posibles de un dispositivo"},"EstadoSalud":{"properties":{"estado":{"type":"string","title":"Estado","description":"'ok' o 'degraded'."},"base_datos":{"type":"string","title":"Base Datos","description":"'ok' si la BD responde; 'error' si falla."},"mensaje":{"type":"string","title":"Mensaje"}},"type":"object","required":["estado","base_datos","mensaje"],"title":"EstadoSalud","description":"Respuesta del health check."},"GrupoActualizar":{"properties":{"nombre":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Nombre"},"descripcion":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Descripcion"},"politica":{"anyOf":[{"$ref":"#/components/schemas/PoliticaGrupo"},{"type":"null"}]}},"type":"object","title":"GrupoActualizar"},"GrupoBasicoDispositivo":{"properties":{"id":{"type":"integer","title":"Id"},"nombre":{"type":"string","title":"Nombre"},"descripcion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Descripcion"}},"type":"object","required":["id","nombre"],"title":"GrupoBasicoDispositivo","description":"Vista reducida de grupo incluida en los detalles de dispositivo."},"GrupoBasicoToken":{"properties":{"id":{"type":"integer","title":"Id"},"nombre":{"type":"string","title":"Nombre"},"descripcion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Descripcion"}},"type":"object","required":["id","nombre"],"title":"GrupoBasicoToken"},"GrupoConDispositivos":{"properties":{"nombre":{"type":"string","maxLength":255,"minLength":1,"title":"Nombre"},"descripcion":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Descripcion"},"id":{"type":"integer","title":"Id"},"politica":{"anyOf":[{"$ref":"#/components/schemas/PoliticaGrupo"},{"type":"null"}]},"fecha_creacion":{"type":"string","format":"date-time","title":"Fecha Creacion"},"fecha_actualizacion":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Actualizacion"},"total_dispositivos":{"type":"integer","title":"Total Dispositivos","default":0},"dispositivos":{"items":{"type":"integer"},"type":"array","title":"Dispositivos","description":"IDs de dispositivos asociados. Para no-admins sólo se listan los suyos."}},"type":"object","required":["nombre","id","fecha_creacion"],"title":"GrupoConDispositivos","description":"Detalle del grupo con los dispositivos asociados visibles al usuario."},"GrupoCrear":{"properties":{"nombre":{"type":"string","maxLength":255,"minLength":1,"title":"Nombre"},"descripcion":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Descripcion"},"politica":{"anyOf":[{"$ref":"#/components/schemas/PoliticaGrupo"},{"type":"null"}]}},"type":"object","required":["nombre"],"title":"GrupoCrear"},"GrupoRespuesta":{"properties":{"nombre":{"type":"string","maxLength":255,"minLength":1,"title":"Nombre"},"descripcion":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Descripcion"},"id":{"type":"integer","title":"Id"},"politica":{"anyOf":[{"$ref":"#/components/schemas/PoliticaGrupo"},{"type":"null"}]},"fecha_creacion":{"type":"string","format":"date-time","title":"Fecha Creacion"},"fecha_actualizacion":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Actualizacion"}},"type":"object","required":["nombre","id","fecha_creacion"],"title":"GrupoRespuesta"},"InformacionServidor":{"properties":{"version":{"type":"string","title":"Version","description":"Versión de la API."},"version_name":{"type":"string","title":"Version Name","description":"Nombre descriptivo de la versión."},"intervalo_checkin_minutos":{"type":"integer","maximum":1440.0,"minimum":1.0,"title":"Intervalo Checkin Minutos","description":"Intervalo por defecto (minutos) entre check-ins."},"enrollment_requiere_token":{"type":"boolean","title":"Enrollment Requiere Token","description":"Si True, /api/publico/enroll exige un token de enrollment válido."},"politica_efectiva":{"$ref":"#/components/schemas/PoliticaEfectiva","description":"Política por defecto del servidor (sin contexto de dispositivo)."}},"type":"object","required":["version","version_name","intervalo_checkin_minutos","enrollment_requiere_token","politica_efectiva"],"title":"InformacionServidor","description":"Información pública del servidor MDM."},"Login":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"Login","description":"Credenciales de login.","example":{"email":"admin@mdm.local","password":"Ejemplo-SuperSegura-2025"}},"PoliticaEfectiva":{"properties":{"intervalo_checkin_minutos":{"type":"integer","maximum":1440.0,"minimum":1.0,"title":"Intervalo Checkin Minutos"},"telemetria":{"$ref":"#/components/schemas/TelemetriaPolitica"},"comandos_permitidos":{"items":{"type":"string"},"type":"array","title":"Comandos Permitidos"},"offline_umbral_minutos":{"type":"integer","maximum":10080.0,"minimum":1.0,"title":"Offline Umbral Minutos"}},"type":"object","required":["intervalo_checkin_minutos","telemetria","comandos_permitidos","offline_umbral_minutos"],"title":"PoliticaEfectiva","description":"Política resuelta para un dispositivo o defaults del servidor."},"PoliticaGrupo":{"properties":{"intervalo_checkin_minutos":{"anyOf":[{"type":"integer","maximum":1440.0,"minimum":1.0},{"type":"null"}],"title":"Intervalo Checkin Minutos"},"telemetria":{"anyOf":[{"$ref":"#/components/schemas/TelemetriaPolitica"},{"type":"null"}]},"comandos_permitidos":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Comandos Permitidos"},"offline_umbral_minutos":{"anyOf":[{"type":"integer","maximum":10080.0,"minimum":1.0},{"type":"null"}],"title":"Offline Umbral Minutos"}},"type":"object","title":"PoliticaGrupo","description":"Política almacenada en grupo; null en intervalo/comandos → default servidor."},"Rol":{"type":"string","enum":["admin","usuario"],"title":"Rol","description":"Roles válidos del sistema."},"TelemetriaPolitica":{"properties":{"ubicacion":{"type":"boolean","title":"Ubicacion","default":true},"apps_instaladas":{"type":"boolean","title":"Apps Instaladas","default":true},"cpu":{"type":"boolean","title":"Cpu","default":true}},"type":"object","title":"TelemetriaPolitica"},"TelemetriaRespuesta":{"properties":{"id":{"type":"integer","title":"Id"},"bateria_porcentaje":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Bateria Porcentaje"},"temperatura":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Temperatura"},"cpu_uso_porcentaje":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Cpu Uso Porcentaje"},"memoria_disponible_mb":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Memoria Disponible Mb"},"almacenamiento_disponible_mb":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Almacenamiento Disponible Mb"},"almacenamiento_total_mb":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Almacenamiento Total Mb"},"version_android":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Android"},"aplicaciones_instaladas":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Aplicaciones Instaladas"},"datos_adicionales":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Datos Adicionales"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"}},"type":"object","required":["id","timestamp"],"title":"TelemetriaRespuesta"},"Token":{"properties":{"access_token":{"type":"string","title":"Access Token"},"token_type":{"type":"string","title":"Token Type","default":"bearer"}},"type":"object","required":["access_token"],"title":"Token","description":"Respuesta al login: token JWT portador.","example":{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1p...","token_type":"bearer"}},"TokenEnrollmentConRelaciones":{"properties":{"id":{"type":"integer","title":"Id"},"token_enrollment":{"type":"string","title":"Token Enrollment","description":"Valor del token."},"nombre_prefijo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nombre Prefijo"},"descripcion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Descripcion"},"usuario_creador_id":{"type":"integer","title":"Usuario Creador Id"},"usuario_asignar_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usuario Asignar Id"},"grupo_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Grupo Id"},"usado":{"type":"boolean","title":"Usado"},"activo":{"type":"boolean","title":"Activo"},"dispositivo_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dispositivo Id"},"fecha_expiracion":{"type":"string","format":"date-time","title":"Fecha Expiracion"},"fecha_creacion":{"type":"string","format":"date-time","title":"Fecha Creacion"},"fecha_uso":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Uso"},"usuario_creador":{"anyOf":[{"$ref":"#/components/schemas/UsuarioBasicoToken"},{"type":"null"}]},"usuario_asignar":{"anyOf":[{"$ref":"#/components/schemas/UsuarioBasicoToken"},{"type":"null"}]},"grupo":{"anyOf":[{"$ref":"#/components/schemas/GrupoBasicoToken"},{"type":"null"}]}},"type":"object","required":["id","token_enrollment","usuario_creador_id","usado","activo","fecha_expiracion","fecha_creacion"],"title":"TokenEnrollmentConRelaciones","description":"Token con relaciones embebidas (creador, asignado, grupo)."},"TokenEnrollmentCrear":{"properties":{"usuario_asignar_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usuario Asignar Id","description":"Usuario al que se asignará el dispositivo al enrollar."},"grupo_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Grupo Id","description":"Grupo al que se añadirá el dispositivo al enrollar."},"nombre_prefijo":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Nombre Prefijo","description":"Prefijo antepuesto al nombre que envíe la app."},"validez_horas":{"type":"integer","maximum":720.0,"minimum":1.0,"title":"Validez Horas","description":"Horas de validez del token (1-720).","default":24},"descripcion":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Descripcion"}},"type":"object","title":"TokenEnrollmentCrear","description":"Entrada para crear un token de enrollment.","example":{"descripcion":"Alta masiva de tablets de la sucursal norte","grupo_id":3,"nombre_prefijo":"SucursalNorte-","usuario_asignar_id":5,"validez_horas":24}},"TokenEnrollmentGenerado":{"properties":{"id":{"type":"integer","title":"Id"},"token_enrollment":{"type":"string","title":"Token Enrollment"},"qr_data":{"$ref":"#/components/schemas/TokenEnrollmentQR"},"fecha_expiracion":{"type":"string","format":"date-time","title":"Fecha Expiracion"},"validez_horas":{"type":"integer","title":"Validez Horas"},"usuario_asignar_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usuario Asignar Id"},"grupo_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Grupo Id"},"nombre_prefijo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nombre Prefijo"},"activo":{"type":"boolean","title":"Activo"}},"type":"object","required":["id","token_enrollment","qr_data","fecha_expiracion","validez_horas","activo"],"title":"TokenEnrollmentGenerado","description":"Respuesta al generar un token de enrollment.","example":{"activo":true,"fecha_expiracion":"2026-04-17T08:30:00Z","grupo_id":3,"id":11,"nombre_prefijo":"SucursalNorte-","qr_data":{"api_url":"https://mdm.example.com","enrollment_token":"enr_6f5d8c7b4a3e2110aabb"},"token_enrollment":"enr_6f5d8c7b4a3e2110aabb","usuario_asignar_id":5,"validez_horas":24}},"TokenEnrollmentInfo":{"properties":{"valido":{"type":"boolean","title":"Valido"},"token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"},"fecha_expiracion":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Expiracion"},"usuario_asignar":{"anyOf":[{"$ref":"#/components/schemas/UsuarioBasicoToken"},{"type":"null"}]},"grupo":{"anyOf":[{"$ref":"#/components/schemas/GrupoBasicoToken"},{"type":"null"}]},"nombre_prefijo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nombre Prefijo"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["valido"],"title":"TokenEnrollmentInfo","description":"Información pública de validez de un token (consumida por la app)."},"TokenEnrollmentLista":{"properties":{"total":{"type":"integer","title":"Total"},"tokens":{"items":{"$ref":"#/components/schemas/TokenEnrollmentConRelaciones"},"type":"array","title":"Tokens"}},"type":"object","required":["total","tokens"],"title":"TokenEnrollmentLista","description":"Listado paginado de tokens."},"TokenEnrollmentQR":{"properties":{"api_url":{"type":"string","title":"Api Url","description":"URL base pública de la API."},"enrollment_token":{"type":"string","title":"Enrollment Token","description":"Token de enrollment."}},"type":"object","required":["api_url","enrollment_token"],"title":"TokenEnrollmentQR","description":"Contenido que se codifica en el QR de enrollment."},"TokenEnrollmentRespuesta":{"properties":{"id":{"type":"integer","title":"Id"},"token_enrollment":{"type":"string","title":"Token Enrollment","description":"Valor del token."},"nombre_prefijo":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nombre Prefijo"},"descripcion":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Descripcion"},"usuario_creador_id":{"type":"integer","title":"Usuario Creador Id"},"usuario_asignar_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Usuario Asignar Id"},"grupo_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Grupo Id"},"usado":{"type":"boolean","title":"Usado"},"activo":{"type":"boolean","title":"Activo"},"dispositivo_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dispositivo Id"},"fecha_expiracion":{"type":"string","format":"date-time","title":"Fecha Expiracion"},"fecha_creacion":{"type":"string","format":"date-time","title":"Fecha Creacion"},"fecha_uso":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Uso"}},"type":"object","required":["id","token_enrollment","usuario_creador_id","usado","activo","fecha_expiracion","fecha_creacion"],"title":"TokenEnrollmentRespuesta","description":"Representación de un token de enrollment."},"UbicacionRespuesta":{"properties":{"id":{"type":"integer","title":"Id"},"latitud":{"type":"number","title":"Latitud"},"longitud":{"type":"number","title":"Longitud"},"precision":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Precision"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"}},"type":"object","required":["id","latitud","longitud","timestamp"],"title":"UbicacionRespuesta"},"UsuarioActualizar":{"properties":{"email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Email"},"nombre":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Nombre"},"password":{"anyOf":[{"type":"string","maxLength":72,"minLength":8},{"type":"null"}],"title":"Password","description":"Contraseña nueva (máximo 72 caracteres)."},"rol":{"anyOf":[{"$ref":"#/components/schemas/Rol"},{"type":"null"}]},"activo":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Activo"}},"type":"object","title":"UsuarioActualizar","description":"Entrada para actualizar un usuario existente."},"UsuarioBasicoRespuesta":{"properties":{"id":{"type":"integer","title":"Id"},"email":{"type":"string","title":"Email"},"nombre":{"type":"string","title":"Nombre"},"rol":{"type":"string","title":"Rol"}},"type":"object","required":["id","email","nombre","rol"],"title":"UsuarioBasicoRespuesta","description":"Vista reducida de usuario incluida en los detalles de dispositivo."},"UsuarioBasicoToken":{"properties":{"id":{"type":"integer","title":"Id"},"email":{"type":"string","title":"Email"},"nombre":{"type":"string","title":"Nombre"}},"type":"object","required":["id","email","nombre"],"title":"UsuarioBasicoToken"},"UsuarioCrear":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"nombre":{"type":"string","maxLength":255,"minLength":1,"title":"Nombre"},"password":{"type":"string","maxLength":72,"minLength":8,"title":"Password","description":"Contraseña (máximo 72 caracteres, límite de bcrypt)."},"rol":{"$ref":"#/components/schemas/Rol","default":"usuario"}},"type":"object","required":["email","nombre","password"],"title":"UsuarioCrear","description":"Entrada para crear un usuario.","example":{"email":"juan.perez@empresa.com","nombre":"Juan Pérez","password":"Ejemplo-SuperSegura-2025","rol":"usuario"}},"UsuarioRespuesta":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"nombre":{"type":"string","maxLength":255,"minLength":1,"title":"Nombre"},"id":{"type":"integer","title":"Id"},"rol":{"type":"string","title":"Rol"},"activo":{"type":"boolean","title":"Activo"},"fecha_creacion":{"type":"string","format":"date-time","title":"Fecha Creacion"},"fecha_actualizacion":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Fecha Actualizacion"}},"type":"object","required":["email","nombre","id","rol","activo","fecha_creacion"],"title":"UsuarioRespuesta","description":"Representación pública del usuario.","example":{"activo":true,"email":"juan.perez@empresa.com","fecha_creacion":"2026-04-16T08:30:00Z","id":1,"nombre":"Juan Pérez","rol":"usuario"}}},"securitySchemes":{"OAuth2PasswordBearer":{"type":"oauth2","flows":{"password":{"scopes":{},"tokenUrl":"/api/auth/login"}}}}},"tags":[{"name":"Sistema","description":"Endpoints meta (raíz, health, version)."},{"name":"Autenticación","description":"Login JWT y perfil del usuario."},{"name":"Usuarios","description":"Gestión de usuarios del panel (admin)."},{"name":"Grupos","description":"Gestión de grupos de dispositivos."},{"name":"Dispositivos","description":"Gestión de dispositivos Android desde el panel."},{"name":"Tokens de Enrollment","description":"Generación y gestión de tokens para enrollar dispositivos."},{"name":"Público","description":"Endpoints consumidos por la app Android (check-in, enrollment, info)."},{"name":"Comandos","description":"Catálogo de tipos de comando y cola MDM (panel + dispositivo)."},{"name":"Auditoría","description":"Registro de acciones administrativas (solo admin)."}]}