Aether Panel Documentation

Referencia de API

Nota: El proyecto tiene los nombres Aether Panel (público) y SkyPanel (código, CLI, módulo Go v3).

Documentación OpenAPI/Swagger interactiva disponible en http://localhost:8080/swagger/index.html cuando el panel está en ejecución.


Tabla de Contenidos


Autenticación

1. OAuth2 Client Credentials (API externa)

POST /oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=ID&client_secret=SECRET

Respuesta:

{
  "access_token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "server.view server.start"
}

Usar el token en todas las peticiones:

Authorization: Bearer eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...

2. OAuth2 Password Grant (SFTP/SSH)

POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer <node-token>

grant_type=password&username=email%23serverId&password=USER_PASSWORD&scope=sftp

3. API Keys (Provision)

X-Api-Key: ak_<key>

O como Bearer:

Authorization: Bearer ak_<key>

4. Auth de Panel (Sesiones Web)

Usar los endpoints /auth/login, /auth/otp, etc. para obtener cookies de sesión.


Scopes (Permisos)

Scopes de Servidor (por servidor)

ScopeDescripción
server.viewVer servidor
server.adminAdmin del servidor
server.deleteEliminar servidor
server.definition.editEditar definición
server.definition.viewVer definición
server.data.editEditar datos del servidor
server.data.edit.adminEditar datos (admin)
server.data.viewVer datos
server.flags.editEditar flags
server.flags.viewVer flags
server.name.editCambiar nombre
server.clients.viewVer clients OAuth2
server.clients.editEditar clients
server.clients.createCrear clients
server.clients.deleteEliminar clients
server.users.viewVer usuarios del servidor
server.users.createAgregar usuarios
server.users.editEditar permisos de usuarios
server.users.deleteEliminar usuarios del servidor
server.tasks.viewVer tareas
server.tasks.runEjecutar tarea
server.tasks.createCrear tarea
server.tasks.deleteEliminar tarea
server.tasks.editEditar tarea
server.reloadRecargar servidor
server.startIniciar servidor
server.stopDetener servidor
server.killMatar proceso
server.installEjecutar instalación
server.files.viewVer archivos
server.files.editSubir/editar/eliminar archivos
server.sftpAcceso SFTP
server.consoleVer consola
server.console.sendEnviar comandos
server.statsVer estadísticas
server.statusVer estado
server.backup.viewVer backups
server.backup.createCrear backup
server.backup.restoreRestaurar backup
server.backup.deleteEliminar backup
server.admin.viewAdmin: ver
server.admin.install.viewAdmin: ver instalación
server.admin.install.manageAdmin: gestionar instalación
server.admin.transfer.viewAdmin: ver transferencias
server.admin.transfer.manageAdmin: gestionar transferencias
server.admin.config.viewAdmin: ver config
server.admin.config.manageAdmin: gestionar config
server.admin.assignments.viewAdmin: ver asignaciones
server.admin.assignments.manageAdmin: gestionar asignaciones

Scopes Globales

ScopeDescripción
adminSuperadmin
loginIniciar sesión
panelAcceso al panel
oauth2.authValidar credenciales vía OAuth2
nodes.viewVer nodos
nodes.createCrear nodos
nodes.editEditar nodos
nodes.deleteEliminar nodos
nodes.deployObtener datos de despliegue
self.editEditar propio perfil
self.clientsGestionar clients OAuth2 propios
settings.editEditar configuración global
templates.viewVer plantillas
templates.local.editEditar plantillas locales
templates.repo.createAgregar repositorios
templates.repo.deleteEliminar repositorios
users.info.searchBuscar usuarios
users.info.viewVer usuarios
users.info.editCrear/editar/eliminar usuarios
users.perms.viewVer permisos de usuarios
users.perms.editEditar permisos de usuarios
uptime.viewVer estadísticas de uptime
server.createCrear servidores

Formato de Respuestas

Éxito

{ "data": { ... } }

O directamente un array u objeto según el endpoint.

Error

{
  "error": {
    "code": "ErrFieldRequired",
    "msg": "username: required field is missing"
  }
}

Códigos de error: ErrFieldRequired, ErrFieldInvalid, ErrServerNotFound, ErrUserNotFound, ErrNodeNotFound, ErrDatabaseError, ErrPermissionDenied, ErrEmailAlreadyUsed, ErrUnknownError.

Códigos HTTP

CódigoSignificado
200OK
201Created
204No Content
202Accepted (operación asíncrona)
400Bad Request
401Unauthorized
403Forbidden
404Not Found
429Too Many Requests
500Internal Server Error

Tipos de Datos

Paging

{ "page": 1, "size": 25, "maxSize": 100, "total": 1 }

Error

{
  "error": { "code": "ErrFieldRequired", "msg": "...", "metadata": {} }
}

Node

{
  "id": 1, "name": "Node-01", "isLocal": true,
  "publicHost": "node1.example.com", "publicPort": 8080,
  "privateHost": "192.168.1.10", "privatePort": 8080,
  "sftpPort": 5657
}

ServerInfo

{
  "id": "abc123", "name": "Minecraft", "node": { "id": 1, "name": "Node" },
  "nodeId": 1, "ip": "192.168.1.100", "port": 25565, "type": "minecraft-java",
  "icon": "minecraft.png", "isGhost": false, "canGetStatus": true,
  "users": [ { "username": "admin", "scopes": ["server.view"] } ]
}

ServerDefinition (Create/Edit)

{
  "name": "Mi Servidor", "type": "minecraft-java", "icon": "minecraft.png",
  "environment": { "type": "standard" },
  "install": [ { "type": "mojangdl", "version": "1.20.1" } ],
  "uninstall": [],
  "run": {
    "command": "java -Xmx{{memory}}M -jar server.jar nogui",
    "stop": "stop", "stopCode": 0,
    "pre": [], "post": [],
    "environmentVars": { "KEY": "VALUE" },
    "workingDirectory": "",
    "autostart": false, "autorestart": false, "autorecover": false,
    "expectedExitCode": 0,
    "stdin": { "type": "", "ip": "", "port": "", "password": "" }
  },
  "data": {
    "memory": { "type": "integer", "value": 2048, "required": true, "desc": "Memoria MB", "display": "Memoria" },
    "port": { "type": "string", "value": "25565", "required": true },
    "version": { "type": "string", "value": "1.20.1", "required": true }
  },
  "groups": [ { "display": "Config", "variables": ["memory", "port"] } ],
  "requirements": { "os": "linux", "arch": "amd64", "binaries": ["java"] },
  "supportedEnvironments": [ { "type": "standard" }, { "type": "docker" } ],
  "keepAlive": { "command": "", "frequency": "" },
  "query": { "type": "" },
  "stats": { "type": "" }
}

ServerData

{
  "data": { "version": { "type": "string", "value": "1.20.1" } },
  "groups": [ { "display": "Config", "variables": ["memory", "port"] } ]
}

ServerRunning (Status)

{ "running": true, "installing": false }

ServerStats

{
  "cpu": 45.2, "memory": 1536000000, "maxMemory": 2147483648,
  "running": true, "storage": 5000000000,
  "jvm": { "heapUsed": 1000000000, "heapTotal": 2000000000, "metaspaceUsed": 50000000, "metaspaceTotal": 100000000 }
}

ServerLogs

{ "logs": ["[10:30:15] [Server thread/INFO]: Starting server"], "epoch": 1705312215 }

ServerFlags

{ "autoStart": false, "autoRestartOnCrash": true, "autoRestartOnGraceful": false }

ServerTask

{
  "name": "Backup diario", "description": "Ejecuta backup cada 6h",
  "cronSchedule": "0 */6 * * *",
  "operations": [ { "type": "backup" } ]
}

User

{ "id": 1, "username": "admin", "email": "admin@example.com", "otpActive": false, "roleId": null, "scopes": [] }

Permissions

{ "serverIdentifier": "abc123", "scopes": [ { "value": "server.view", "forServer": true } ] }

Backup

{ "id": 1, "name": "Backup 2024-01-15", "fileName": "backup_abc123.tar.gz", "createdAt": "2024-01-15T10:30:00Z" }

Features

{ "arch": "amd64", "os": "linux", "version": "1.0.0", "environments": ["standard", "docker"], "features": ["docker"] }

SystemInfo

{
  "hostname": "node1", "os": "linux", "platform": "ubuntu", "platformVersion": "22.04",
  "arch": "amd64", "uptime": 123456,
  "cpuModel": "Intel Core", "cpuCores": 8, "cpuThreads": 16, "cpuUsage": 25.5,
  "memoryTotal": 17179869184, "memoryUsed": 8589934592, "memoryFree": 8589934592,
  "disks": [ { "path": "/", "total": 1099511627776, "used": 549755813888, "free": 549755813888, "usedPercent": 50.0 } ],
  "networkBytesSent": 1000000, "networkBytesRecv": 2000000
}

DatabaseHostView

{
  "id": 1, "name": "DB Host 1", "host": "db1.example.com", "port": 3306,
  "username": "skypanel", "node_id": 1, "max_databases": 10,
  "created_at": "...", "updated_at": "..."
}

DatabaseView

{
  "id": 1, "server_id": "abc123", "database_host_id": 1, "database_name": "server_abc123",
  "host": "db1.example.com", "port": 3306, "username": "user_abc123",
  "password": "pass123", "max_connections": 5, "remote_connection": "",
  "host_name": "DB Host 1", "created_at": "...", "updated_at": "..."
}

Template

{
  "id": "minecraft-java", "name": "Minecraft Java Edition", "display": "Minecraft Java",
  "type": "minecraft-java", "icon": "minecraft.png",
  "install": [], "run": { "command": "" }, "data": {},
  "environment": { "type": "standard" }
}

TemplateRepo

{ "id": 1, "name": "official", "url": "https://templates.example.com/templates.json", "branch": "main", "isLocal": false }

Role

{ "id": 1, "name": "Admin", "description": "Full access", "scopes": ["admin"], "createdAt": "...", "updatedAt": "..." }

Client (OAuth2)

{ "client_id": "abc123", "client_secret": "secret...", "name": "My App", "description": "App description" }

PluginInfo

{ "name": "EssentialsX.jar", "version": "2.20.1", "size": 1234567 }

PluginSearchResult

{
  "id": "essentialssx", "name": "EssentialsX", "tag": "essentialsx",
  "version": "2.20.1", "author": "EssentialsX Team",
  "description": "Essential commands...", "iconUrl": "...", "downloads": 1000000
}

Config

GET /api/config

Sin autenticación. Retorna configuración pública del panel.

{
  "branding": { "name": "SkyPanel" },
  "registrationEnabled": true,
  "themes": { "active": "default", "available": ["alternativeTheme"], "settings": "{}" }
}

Auth

POST /auth/login

Body:

{ "email": "admin@example.com", "password": "..." }

Respuesta:

{ "otpNeeded": false, "token": "session_token" }

Si otpNeeded es true, continuar con /auth/otp.

POST /auth/otp

Body:

{ "token": "session_token_from_login", "otp": "123456" }

Respuesta:

{ "token": "final_session_token", "otpNeeded": false }

POST /auth/logout

Cierra la sesión actual.

POST /auth/register

Requiere registrationEnabled: true. Body:

{ "username": "newuser", "email": "new@example.com", "password": "Secure123!" }

POST /auth/reauth

Re-autentica la sesión actual. Auth: Bearer.

GET /auth/publickey

Retorna la clave pública Ed25519 en formato JWK para validar JWTs.


OAuth2

POST /oauth2/token

Form (urlencoded): grant_type, client_id, client_secret, username, password

Ver Autenticación para ejemplos.

Errores:

{ "error": "invalid_client", "error_description": "Invalid client credentials" }

Nodos

MétodoPathScopeDescripción
GET/api/nodesnodes.viewListar nodos
POST/api/nodesnodes.createCrear nodo
GET/api/nodes/:idnodes.viewObtener nodo
PUT/api/nodes/:idnodes.editActualizar nodo
DELETE/api/nodes/:idnodes.deleteEliminar nodo
GET/api/nodes/:id/featuresnodes.viewFeatures del nodo
GET/api/nodes/:id/systemnodes.viewInfo del sistema
GET/api/nodes/:id/deploymentnodes.deployDatos de despliegue

POST /api/nodes

Body:

{
  "name": "Node-02", "publicHost": "node2.example.com", "privateHost": "192.168.1.11",
  "publicPort": 8080, "privatePort": 8080, "sftpPort": 5657
}

Respuesta: Node (incluye id)

GET /api/nodes/:id/deployment

{ "clientId": ".node_1", "clientSecret": "abc123def456...", "publicKey": "..." }

Servidores

La mayoría de los endpoints de acción usan proxyServerRequest que reenvía la petición al daemon del nodo.

MétodoPathScopeDescripción
GET/api/servers(auth)Listar servidores
GET/api/servers/:serverIdserver.viewObtener servidor
PUT/api/servers/:serverIdserver.createCrear servidor
DELETE/api/servers/:serverIdserver.deleteEliminar servidor
POST/api/servers/:serverId/suspendserver.edit.data.adminSuspender/activar
PUT/api/servers/:serverId/name/:nameserver.name.editRenombrar
GET/api/servers/:serverId/definitionserver.definition.viewObtener definición
PUT/api/servers/:serverId/definitionserver.definition.editEditar definición
GET/api/servers/:serverId/dataserver.data.viewObtener variables
POST/api/servers/:serverId/dataserver.data.editEditar variables
PUT/api/servers/:serverId/dataserver.data.edit.adminEditar datos (admin)
POST/api/servers/:serverId/transferserver.edit.data.adminTransferir a otro nodo
GET/api/servers/:serverId/statusserver.statusEstado (running/stopped)
GET/api/servers/:serverId/statsserver.statsEstadísticas
GET/api/servers/:serverId/consoleserver.consoleLogs de consola
POST/api/servers/:serverId/consoleserver.console.sendEnviar comando
GET/api/servers/:serverId/flagsserver.flags.viewObtener flags
POST/api/servers/:serverId/flagsserver.flags.editEditar flags
POST/api/servers/:serverId/startserver.startIniciar
POST/api/servers/:serverId/stopserver.stopDetener
POST/api/servers/:serverId/restartserver.start+server.stopReiniciar
POST/api/servers/:serverId/killserver.killMatar proceso
POST/api/servers/:serverId/installserver.installEjecutar instalación
POST/api/servers/:serverId/reloadserver.reloadRecargar configuración
HEAD/api/servers/:serverId/queryserver.statsConsultar(query) server
GET/api/servers/:serverId/queryserver.statsConsultar server
GET/api/servers/:serverId/socketserver.viewWebSocket (consola/stats)

GET /api/servers

Query params: name (filtro con *), node (ID), username, page, limit.

{
  "servers": [ { "identifier": "abc123", "name": "Server", "node": { "id": 1, "name": "Node" }, "ip": "10.0.0.1", "port": 25565, "type": "minecraft-java", "canGetStatus": true } ],
  "metadata": { "paging": { "page": 1, "size": 25, "maxSize": 100, "total": 1 } }
}

GET /api/servers/:serverId?perms=true

{
  "server": { "identifier": "abc123", "name": "Server", "icon": "minecraft.png", "node": { "id": 1, "name": "Node" } },
  "perms": { "scopes": [{ "value": "server.view", "forServer": true }] }
}

PUT /api/servers/:serverId

Crea un servidor. Body: ServerDefinition (ver tipos). Respuesta: { "id": "abc123" }

DELETE /api/servers/:serverId?skipNode=true

skipNode: elimina solo de la BD, no del nodo.

POST /api/servers/:serverId/suspend

Suspende o activa el servidor (toggle).

PUT /api/servers/:serverId/name/:name

Renombra el servidor en la BD.

GET /api/servers/:serverId/definition

Retorna la definición completa del servidor.

PUT /api/servers/:serverId/definition

Body: ServerDefinition. Respuesta: 204.

GET /api/servers/:serverId/data

{ "data": { "version": { "type": "string", "value": "1.20.1" } }, "groups": [] }

POST /api/servers/:serverId/data

Edita variables del servidor. Body: { "key": "value" } (objeto plano). Respuesta: 202.

PUT /api/servers/:serverId/data

Edición admin de datos. Body: { ... }. Respuesta: 202.

POST /api/servers/:serverId/transfer

Body: { "nodeId": 2 }. Transfiere el servidor a otro nodo. Respuesta: 202 "Transfer started".

GET /api/servers/:serverId/status

{ "running": true, "installing": false }

GET /api/servers/:serverId/stats

{
  "cpu": 45.2, "memory": 1536000000, "maxMemory": 2147483648,
  "running": true, "storage": 5000000000,
  "jvm": { "heapUsed": 1000000000, "heapTotal": 2000000000, "metaspaceUsed": 50000000, "metaspaceTotal": 100000000 }
}

GET /api/servers/:serverId/console?time=1705312215000

time: epoch en ms para obtener logs desde ese momento.

{ "logs": ["[10:30:15] [Server thread/INFO]: Starting server"], "epoch": 1705312215 }

POST /api/servers/:serverId/console

Body: "command" (string literal del comando). Respuesta: 204.

GET /api/servers/:serverId/flags

{ "autoStart": false, "autoRestartOnCrash": true, "autoRestartOnGraceful": false }

POST /api/servers/:serverId/flags

Body: ServerFlags. Respuesta: 204.

Acciones de ciclo de vida

AcciónMétodoRespuesta
IniciarPOST /api/servers/:serverId/start202 / 204
DetenerPOST /api/servers/:serverId/stop202 / 204
ReiniciarPOST /api/servers/:serverId/restart202 / 204
MatarPOST /api/servers/:serverId/kill204
InstalarPOST /api/servers/:serverId/install202 / 204
RecargarPOST /api/servers/:serverId/reload204

HEAD / GET /api/servers/:serverId/query

Consulta el servidor de juego vía query protocol.


Archivos

MétodoPathScopeDescripción
GET/api/servers/:serverId/file/*filenameserver.files.viewListar/descargar archivo
PUT/api/servers/:serverId/file/*filenameserver.files.editSubir archivo
DELETE/api/servers/:serverId/file/*filenameserver.files.editEliminar archivo
POST/api/servers/:serverId/file/*filenameserver.files.editMover/copiar archivo

Listar directorio: GET /api/servers/:serverId/file/

{
  "files": [
    { "name": "server.jar", "size": 45678901, "modified": "2024-01-15T10:30:00Z", "isFile": true },
    { "name": "world", "size": 0, "modified": "2024-01-15T10:25:00Z", "isFile": false }
  ]
}

Subir: PUT /api/servers/:serverId/file/config.yml con Content-Type: application/octet-stream.


Archivos Comprimidos

MétodoPathScopeDescripción
HEAD/api/servers/:serverId/archive/*filenameserver.files.editVerificar si existe
POST/api/servers/:serverId/archive/*filenameserver.files.editCrear ZIP
POST/api/servers/:serverId/extract/*filenameserver.files.editExtraer ZIP

POST /api/servers/:serverId/archive/backup.zip

Body: ["file1.txt", "folder/"] — archivos a comprimir. Query: destination — subdirectorio de destino. Respuesta: 204.

POST /api/servers/:serverId/extract/archive.zip

Query: destination — directorio donde extraer (vacío = raíz del servidor). Respuesta: 204.


Plugins

MétodoPathScope
GET/api/servers/:serverId/pluginsserver.files.view
DELETE/api/servers/:serverId/pluginsserver.files.edit
GET/api/servers/:serverId/plugins/searchserver.files.view
POST/api/servers/:serverId/plugins/:pluginIdserver.files.edit

GET /api/servers/:serverId/plugins

[{ "name": "EssentialsX.jar", "version": "2.20.1", "size": 1234567 }]

GET /api/servers/:serverId/plugins/search?q=essentials

[{ "id": "essentialsx", "name": "EssentialsX", "version": "2.20.1", "author": "...", "downloads": 1000000 }]

DELETE /api/servers/:serverId/plugins?name=EssentialsX.jar

Elimina el plugin EssentialsX.jar.

POST /api/servers/:serverId/plugins/:pluginId

Instala el plugin desde SpigotMC (ID numérico de Spigot).


Backups

MétodoPathScope
GET/api/servers/:serverId/backupserver.backup.view
GET/api/servers/:serverId/backup/:backupIDserver.backup.view
DELETE/api/servers/:serverId/backup/:backupIDserver.backup.delete
POST/api/servers/:serverId/backup/createserver.backup.create
POST/api/servers/:serverId/backup/restore/:backupIDserver.backup.restore
GET/api/servers/:serverId/backup/download/:backupIDserver.backup.view

GET /api/servers/:serverId/backup

[{ "id": 1, "name": "Backup 2024-01-15", "fileName": "backup_abc123.tar.gz", "createdAt": "2024-01-15T10:30:00Z" }]

POST /api/servers/:serverId/backup/create

Respuesta: { "backupFileName": "backup_abc123.tar.gz" }


Tareas (Tasks)

MétodoPathScope
GET/api/servers/:serverId/tasksserver.tasks.view
GET/api/servers/:serverId/tasks/:taskIdserver.tasks.view
PUT/api/servers/:serverId/tasks/:taskIdserver.tasks.edit
DELETE/api/servers/:serverId/tasks/:taskIdserver.tasks.delete
POST/api/servers/:serverId/tasks/:taskId/runserver.tasks.run

GET /api/servers/:serverId/tasks

{
  "tasks": {
    "backup_task": { "name": "Backup diario", "cronSchedule": "0 */6 * * *", "operations": [{ "type": "backup" }] }
  }
}

PUT /api/servers/:serverId/tasks/:taskId

Body: ServerTask. Respuesta: 204.


Bases de Datos del Servidor

MétodoPathScope
GET/api/servers/:serverId/databasesserver.view
POST/api/servers/:serverId/databasesserver.data.edit
DELETE/api/servers/:serverId/databases/:idserver.data.edit

POST /api/servers/:serverId/databases

Body: { "database_host_id": 1, "database_name": "my_db" } Respuesta: DatabaseView (con username/password generados).


Usuarios del Servidor

MétodoPathScope
GET/api/servers/:serverId/userserver.users.view
GET/api/servers/:serverId/user/:emailserver.users.view
PUT/api/servers/:serverId/user/:emailserver.users.edit
DELETE/api/servers/:serverId/user/:emailserver.users.delete

GET /api/servers/:serverId/user

[{ "username": "admin", "email": "admin@example.com", "scopes": ["server.view", "server.console"] }]

PUT /api/servers/:serverId/user/:email

Body: { "permissions": { "scopes": ["server.view", "server.console"] } } Respuesta: 204.


Transferencia entre Nodos

MétodoPathScope
POST/api/servers/:serverId/transferserver.edit.data.admin

Body: { "nodeId": 2 }


Transferencia Externa

Endpoints públicos para migrar servidores entre paneles (sin autenticación).

MétodoPathDescripción
POST/api/extransfer/validateValidar token de transferencia
POST/api/extransfer/consumeConsumir transferencia
POST/api/extransfer/heartbeatHeartbeat durante transferencia
POST/api/extransfer/confirmConfirmar transferencia
GET/api/extransfer/downloadDescargar datos de transferencia
POST/api/extransfer/cancelCancelar transferencia

También desde el servidor:

MétodoPathScope
POST/api/servers/:serverId/extransfer/createserver.edit.data.admin
POST/api/servers/:serverId/extransfer/pullserver.edit.data.admin
GET/api/servers/:serverId/extransfer/statusserver.edit.data.admin

AI

MétodoPathScope
POST/api/ai/analyze (autenticado)
POST/api/servers/:serverId/ai/analyzeserver.console

Analiza logs del servidor usando Google GenAI (requiere geminiApiKey configurada).

POST /api/ai/analyze

// Request
{ "logs": ["[ERROR] Connection refused", "[WARN] Memory low"] }

// Response
{
  "summary": "Resumen del análisis...",
  "rootCauses": ["Causa raíz 1"],
  "suggestions": ["Sugerencia 1", "Sugerencia 2"]
}

Usuarios Globales

MétodoPathScopeDescripción
GET/api/usersusers.info.searchBuscar usuarios
POST/api/usersusers.info.editCrear usuario
GET/api/users/:idusers.info.viewObtener usuario
POST/api/users/:idusers.info.editActualizar usuario
DELETE/api/users/:idusers.info.editEliminar usuario
GET/api/users/:id/permsusers.perms.viewObtener permisos
PUT/api/users/:id/permsusers.perms.editActualizar permisos

GET /api/users?username=admin*&email=*@example.com&page=1&limit=25

{
  "users": [{ "id": 1, "username": "admin", "email": "admin@example.com" }],
  "metadata": { "paging": { "page": 1, "size": 25, "maxSize": 100, "total": 1 } }
}

POST /api/users

Body: { "username": "newuser", "email": "new@example.com", "password": "Secure123!" }

POST /api/users/:id

Body: { "username": "newname", "email": "new@example.com", "password": "newpass" }

GET /api/users/:id/perms

{ "serverIdentifier": "", "scopes": [{ "value": "server.view", "forServer": false }, { "value": "admin", "forServer": false }] }

PUT /api/users/:id/perms

Body: { "scopes": ["admin", "server.view"] } (array de strings). Respuesta: 204.


Self (Perfil Propio)

MétodoPathScopeDescripción
GET/api/selfloginObtener perfil propio
PUT/api/selfself.editActualizar perfil
GET/api/self/otpself.editEstado de OTP
POST/api/self/otpself.editIniciar enrolamiento OTP
PUT/api/self/otpself.editValidar enrolamiento
POST/api/self/otp/recoveryself.editRegenerar códigos de recuperación
DELETE/api/self/otp/:tokenself.editDeshabilitar OTP
GET/api/self/oauth2self.clientsListar OAuth2 clients
POST/api/self/oauth2self.clientsCrear client
DELETE/api/self/oauth2/:clientIDself.clientsEliminar client

Settings

MétodoPathScopeDescripción
GET/api/settingssettings.editObtener configuración
POST/api/settingssettings.editActualizar múltiples valores
GET/api/settings/:keysettings.editObtener un valor
PUT/api/settings/:keysettings.editActualizar un valor
POST/api/settings/test/emailsettings.editEnviar email de prueba
POST/api/settings/test/discordsettings.editEnviar notificación Discord de prueba
POST/api/settings/license/activatesettings.editActivar licencia

POST /api/settings

Body: { "companyName": "Mi Empresa", "registrationEnabled": false }. Respuesta: 204.

PUT /api/settings/:key

Body: { "value": "nuevo_valor" }. Respuesta: 204.


User Settings

MétodoPathScope
GET/api/userSettingslogin
PUT/api/userSettings/:keylogin

PUT /api/userSettings/theme

Body: { "value": "dark" }. Respuesta: 204.


API Keys

MétodoPathScope
GET/api/settings/apikeysadmin
POST/api/settings/apikeysadmin
DELETE/api/settings/apikeys/:idadmin

Roles

MétodoPathScope
GET/api/rolesadmin o users.info.view/edit
POST/api/rolesadmin
GET/api/roles/:idadmin
POST/api/roles/:idadmin
DELETE/api/roles/:idadmin

POST /api/roles

Body: { "name": "Moderator", "description": "Can manage servers", "scopes": ["server.view", "server.start"] }


Database Hosts

MétodoPathScope
GET/api/databasehostsadmin
POST/api/databasehostsadmin
GET/api/databasehosts/:idadmin
PUT/api/databasehosts/:idadmin
DELETE/api/databasehosts/:idadmin

POST /api/databasehosts

Body:

{
  "name": "DB Host 1", "host": "db1.example.com", "port": 3306,
  "username": "skypanel", "password": "secret", "max_databases": 10, "node_id": 1
}

Templates

MétodoPathScope
GET/api/templateslogin
POST/api/templatestemplates.repo.create
GET/api/templates/:repologin
DELETE/api/templates/:repotemplates.repo.delete
GET/api/templates/:repo/:namelogin
PUT/api/templates/0/:nametemplates.local.edit
DELETE/api/templates/0/:nametemplates.local.edit

:repo=0 es el repositorio local. Los repositorios remotos tienen IDs > 0.


Provision Products

MétodoPathScope
GET/api/provision/productsadmin
POST/api/provision/productsadmin
PUT/api/provision/products/:idadmin
DELETE/api/provision/products/:idadmin

Provision API v1

Autenticación por API Key en header. Endpoints para integración con sistemas externos (WHMCS, etc.).

MétodoPathDescripción
GET/api/v1/pingVerificar conectividad
POST/api/v1/provisionCrear servidor automáticamente
POST/api/v1/terminateTerminar servidor
POST/api/v1/suspendSuspender servidor
POST/api/v1/unsuspendReactivar servidor

Uptime

MétodoPathScopeDescripción
GET/api/uptimeadmin o uptime.viewTodos los registros
GET/api/uptime/:serverIdserver.viewRegistros de un servidor

Query params: days (días hacia atrás), limit (número de registros).


Daemon

Endpoints del daemon para comunicación directa entre nodos y panel. No pasan por proxy. Usan autenticación JWT.

MétodoPathScope Swagger
GET/daemonnone
HEAD/daemonnone
GET/daemon/featuresnone
GET/daemon/systemnone
GET/PUT/DELETE/daemon/server/:serverId/...(según acción)

Acciones del Daemon por Servidor

MétodoPathScope Swagger
PUT/daemon/server/:serverId
DELETE/daemon/server/:serverId
GET/daemon/server/:serverId/definitionserver.definition.view
PUT/daemon/server/:serverId/definitionserver.definition.edit
GET/daemon/server/:serverId/dataserver.data.view
POST/daemon/server/:serverId/data
PUT/daemon/server/:serverId/data
GET/daemon/server/:serverId/tasksserver.tasks.view
GET/PUT/DELETE/daemon/server/:serverId/tasks/:taskIDserver.tasks.*
POST/daemon/server/:serverId/tasks/:taskID/runserver.tasks.run
POST/daemon/server/:serverId/reloadserver.reload
POST/daemon/server/:serverId/startserver.start
POST/daemon/server/:serverId/restartserver.start + server.stop
POST/daemon/server/:serverId/stopserver.stop
POST/daemon/server/:serverId/killserver.kill
POST/daemon/server/:serverId/installserver.install
GET/daemon/server/:serverId/file/*filenameserver.files.view
PUT/daemon/server/:serverId/file/*filenameserver.files.edit
DELETE/daemon/server/:serverId/file/*filenameserver.files.edit
GET/daemon/server/:serverId/consoleserver.console
POST/daemon/server/:serverId/consoleserver.console.send
GET/daemon/server/:serverId/flagsserver.flags.view
POST/daemon/server/:serverId/flagsserver.flags.edit
GET/daemon/server/:serverId/statsserver.stats
GET/daemon/server/:serverId/statusserver.status
POST/daemon/server/:serverId/archive/*filenameserver.files.edit
POST/daemon/server/:serverId/extract/*filenameserver.files.edit
POST/daemon/server/:serverId/backup/createserver.backup.create
DELETE/daemon/server/:serverId/backupserver.backup.delete
POST/daemon/server/:serverId/backup/restoreserver.backup.restore
GET/daemon/server/:serverId/backup/downloadserver.backup.restore
HEAD/GET/daemon/server/:serverId/queryserver.query
GET/daemon/server/:serverId/plugins
DELETE/daemon/server/:serverId/plugins
GET/daemon/server/:serverId/plugins/search
POST/daemon/server/:serverId/plugins/:pluginId
GET/daemon/server/:serverId/socket

WebSocket

GET /api/servers/:serverId/socket

Conecta a la consola y estadísticas en tiempo real.

const ws = new WebSocket(`ws://localhost:8080/api/servers/${serverId}/socket?token=${token}`);

Tipos de Mensaje

TipoDirecciónDescripción
authCliente → ServidorAutenticación (alternativa al query param)
consoleServidor → ClienteLínea de consola del servidor
statsServidor → ClienteEstadísticas periódicas
statusServidor → ClienteCambio de estado

Eventos del Servidor

{ "type": "console", "data": "[10:30:15] [Server thread/INFO]: Starting server" }
{ "type": "stats", "data": { "cpu": 45.2, "memory": 1536000000, "memoryTotal": 2147483648 } }
{ "type": "status", "data": { "running": true } }

Enviar Comando

{ "type": "console", "data": "say Hello World!" }

Ejemplos

cURL

TOKEN=$(curl -s -X POST http://localhost:8080/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=ID" \
  -d "client_secret=SECRET" | jq -r '.access_token')

curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/servers
curl -s -X POST -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/servers/abc123/start

Python

import requests
api = "http://localhost:8080"
r = requests.post(f"{api}/oauth2/token", data={"grant_type": "client_credentials", "client_id": "ID", "client_secret": "SECRET"})
token = r.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
servers = requests.get(f"{api}/api/servers", headers=headers).json()

JavaScript

const api = axios.create({ baseURL: 'http://localhost:8080' });
const { data } = await api.post('/oauth2/token', new URLSearchParams({ grant_type: 'client_credentials', client_id: 'ID', client_secret: 'SECRET' }));
api.defaults.headers.Authorization = `Bearer ${data.access_token}`;
const servers = (await api.get('/api/servers')).data;

No olvides que Aether Panel es un proyecto en desarrollo open source, si tienes alguna duda o problema al instalar o el comando del instalador no funciona puedes contactarnos en el Discord de Aether Panel.

    Referencia de API | Aether Panel Docs | Aether Panel — Open Source Hosting Platform