# API Reference ## Overview Muximux provides a REST API for managing configuration, apps, groups, health, authentication, icons, and themes. All endpoints return JSON. When authentication is enabled, most endpoints require a valid session cookie or API key (`X-Api-Key` header). Write operations (POST, PUT, DELETE) require the `admin` role. --- ## Authentication | Endpoint | Method | Auth Required | Description | |----------|--------|---------------|-------------| | `/api/auth/login` | POST | No | Login with username/password | | `/api/auth/logout` | POST | No | Logout (clears session) | | `/api/auth/status` | GET | No | Check auth status and current user | | `/api/auth/me` | GET | Yes | Get current user details | | `/api/auth/password` | POST | Yes | Change password (builtin auth only) | | `/api/auth/users` | GET | Admin | List all users | | `/api/auth/users` | POST | Admin | Create a new user | | `/api/auth/users/{username}` | PUT | Admin | Update user role/email/display name | | `/api/auth/users/{username}` | DELETE | Admin | Delete a user | | `/api/auth/method` | PUT | Admin | Switch authentication method | | `/api/auth/api-key` | GET | Admin | Report whether an instance API key is configured (never returns the key) | | `/api/auth/api-key` | POST | Admin | Generate or rotate the instance API key | | `/api/auth/api-key` | DELETE | Admin | Clear the instance API key | | `/api/auth/setup` | POST | No (but requires `X-Setup-Token`) | Initial setup (onboarding wizard). See [Authentication > First-Run Setup](authentication.md#first-run-setup) for the token. | | `/api/auth/oidc/login` | GET | No | Redirect to OIDC provider | | `/api/auth/oidc/callback` | GET | No | OIDC callback handler | **Login request:** ```json POST /api/auth/login { "username": "admin", "password": "secretpassword" } ``` **Login response:** ```json { "success": true, "user": { "username": "admin", "role": "admin", "email": "admin@example.com", "display_name": "Admin User" } } ``` **Auth status response:** ```json GET /api/auth/status { "authenticated": true, "auth_method": "builtin", "oidc_enabled": false, "setup_required": false, "user": { "username": "admin", "role": "admin", "email": "admin@example.com", "display_name": "Admin User" } } ``` The `auth_method` field returns the active authentication method: `none`, `builtin`, `forward_auth`, or `oidc`. The `user` field is only present when the request is authenticated. When `auth_method` is `forward_auth` and a logout URL is configured, the response includes a `logout_url` field for client-side redirect on sign-out. **API key authentication:** Non-browser integrations can authenticate with an API key via the `X-Api-Key` header, but **only on allowlisted paths** -- not every `/api/*` endpoint. Out of the box: - `GET /api/appearance` -- read Muximux's current theme + language (see [Apps > Appearance API](apps.md#appearance-api)) - Any proxied-app path the operator has opted into via `auth_bypass: [{require_api_key: true}]` (see [Apps > Per-App Auth Bypass](apps.md#per-app-auth-bypass)) Example, calling Sonarr through Muximux with a configured bypass: ``` GET /proxy/sonarr/api/v3/series X-Api-Key: your-api-key-here ``` State-changing endpoints like `/api/config`, `/api/apps`, `/api/themes`, and `/api/auth/users` require a session cookie -- the API key will not work against them. See [Authentication > API Key Authentication](authentication.md#api-key-authentication) for the full scope and setup instructions. ### User Management **List users:** ``` GET /api/auth/users ``` Returns an array of users (without password hashes): ```json [ {"username": "admin", "role": "admin", "email": "admin@example.com", "display_name": "Admin User"}, {"username": "viewer", "role": "user", "email": "", "display_name": ""} ] ``` **Create user:** ```json POST /api/auth/users { "username": "newuser", "password": "minimum8chars", "role": "user", "email": "user@example.com", "display_name": "New User" } ``` Validation: username is required, password must be at least 8 characters. Valid roles: `admin`, `power-user`, `user`. If the role is omitted or invalid, it defaults to `user`. **Update user:** ```json PUT /api/auth/users/newuser { "role": "admin", "email": "updated@example.com", "display_name": "Updated Name" } ``` All fields are optional -- only provided fields are updated. **Delete user:** ``` DELETE /api/auth/users/newuser ``` Constraints: you cannot delete your own account, and you cannot delete the last admin user. ### Auth Method Switching **Switch authentication method:** ```json PUT /api/auth/method { "method": "forward_auth", "trusted_proxies": ["192.168.0.0/16"], "headers": { "user": "Remote-User", "email": "Remote-Email", "groups": "Remote-Groups", "name": "Remote-Name" }, "logout_url": "https://auth.example.com/logout" } ``` Valid methods: `builtin`, `forward_auth`, `none`. Switching to `builtin` requires at least one user to exist. Switching to `forward_auth` requires `trusted_proxies`. The optional `logout_url` configures where the browser redirects on sign-out to clear the auth provider's session. The change takes effect immediately without a restart. --- ## Configuration | Endpoint | Method | Role | Description | |----------|--------|------|-------------| | `/api/config` | GET | Any | Get full configuration | | `/api/config` | PUT | Admin | Update full configuration | | `/api/config/export` | GET | Admin | Download config as YAML (sensitive data stripped) | | `/api/config/import` | POST | Admin | Parse and validate uploaded YAML, returns preview | | `/api/config/restore` | POST | Pre-setup only (requires `X-Setup-Token`) | Replace the live config with an uploaded YAML during first-run onboarding | | `/api/appearance` | GET | Any authenticated user, or any caller with `X-Api-Key` | Read-only snapshot of language + theme + colors for embedded apps | The PUT endpoint accepts the full configuration object. Changes to most settings take effect immediately. Server-level settings (listen address, TLS, gateway) require a restart. Auth method changes can be made live via `PUT /api/auth/method`. **Export config:** ``` GET /api/config/export ``` Returns a downloadable YAML file with password hashes, OIDC client secrets, and API keys removed. The filename includes the current date (e.g., `muximux-config-2025-01-15.yaml`). **Import config (preview):** ``` POST /api/config/import Content-Type: application/x-yaml (YAML body, max 1 MB) ``` Validates the YAML and returns the parsed config as JSON for preview. The frontend can then apply it via `PUT /api/config`. Validation requires at least one app with a name and URL. **Restore config (first-run only):** ``` POST /api/config/restore Content-Type: application/x-yaml X-Setup-Token: (YAML body, max 1 MB) ``` Replaces the live config during the initial onboarding flow. Only accepted while the instance is in the pre-setup state; subsequent calls return `409 Conflict`. Missing or invalid `X-Setup-Token` returns `401`. See [Authentication > First-Run Setup](authentication.md#first-run-setup) for how to find the token. **Appearance (read-only):** ``` GET /api/appearance ``` Returns a JSON snapshot with `language`, `theme` (`family`, `variant`, `id`, `is_dark`), `colors` (a curated subset of CSS custom-property values), and `theme_css_url`. Embedded apps call this once on boot to sync their own styling with Muximux. See [Apps > Appearance API](apps.md#appearance-api) for the full contract and usage examples. --- ## Apps | Endpoint | Method | Role | Description | |----------|--------|------|-------------| | `/api/apps` | GET | Any | List all apps | | `/api/apps` | POST | Admin | Create a new app | | `/api/app/{name}` | GET | Any | Get app by name | | `/api/app/{name}` | PUT | Admin | Update app | | `/api/app/{name}` | DELETE | Admin | Delete app | | `/api/app-action/{name}` | POST | Per-app role gate | Fire the app's configured http_action HTTP request | | `/api/app-docker/{name}/{start\|stop\|restart}` | POST | Role-gated | Control a Docker-tracked container (requires lifecycle_enabled + :rw socket) | **Create app request:** ```json POST /api/apps { "name": "Sonarr", "url": "http://sonarr:8989", "icon": { "type": "dashboard", "name": "sonarr" }, "group": "Media", "enabled": true, "open_mode": "iframe" } ``` --- ## Groups | Endpoint | Method | Role | Description | |----------|--------|------|-------------| | `/api/groups` | GET | Any | List all groups | | `/api/groups` | POST | Admin | Create a new group | | `/api/group/{name}` | GET | Any | Get group by name | | `/api/group/{name}` | PUT | Admin | Update group | | `/api/group/{name}` | DELETE | Admin | Delete group | --- ## Discovery | Endpoint | Method | Role | Description | |----------|--------|------|-------------| | `/api/discovery/docker/status` | GET | Admin | Docker daemon reachability + discovery config | | `/api/discovery/docker/networks` | GET | Admin | List Docker networks for URL resolution | | `/api/discovery/docker/config` | PUT | Admin | Update discovery config (incl. auto_import mode, lifecycle_enabled) | | `/api/discovery/docker/test` | POST | Admin | Test a discovery config without saving | | `/api/discovery/docker/scan` | GET | Admin | Scan the daemon, return importable containers | | `/api/discovery/docker/import` | POST | Admin | Import selected containers as apps | | `/api/discovery/docker/tracked` | GET | Admin | List apps currently tracked from Docker | | `/api/discovery/docker/track/{name}` | DELETE | Admin | Detach an app from Docker tracking | | `/api/discovery/docker/relink/probe` | POST | Admin | Probe a container to re-link a detached app | | `/api/discovery/docker/relink/confirm` | POST | Admin | Confirm a re-link | | `/api/discovery/docker-state` | GET | Any | Current container-state map for tracked apps | Auto-import is opt-in via `discovery.docker.auto_import` (`off`, `add`, `update`, or `sync`). The discovery configuration is also part of the full configuration object, so it can be set via `PUT /api/config` as well as `PUT /api/discovery/docker/config`. --- ## Health | Endpoint | Method | Description | |----------|--------|-------------| | `/api/health` | GET | Simple health probe (for load balancers) | | `/api/apps/health` | GET | Get health status for all apps | | `/api/apps/{name}/health` | GET | Get health status for one app | | `/api/apps/{name}/health/check` | POST | Trigger immediate health check | The `/api/health` endpoint always returns 200 OK when the server is running. It is intended for use with load balancers and uptime monitors. **Health status response:** ```json { "name": "Sonarr", "status": "healthy", "response_time_ms": 42, "last_check": "2024-01-15T10:30:00Z", "uptime_percent": 99.8, "check_count": 1440, "success_count": 1437 } ``` **Status values:** - `healthy` -- Last check received an HTTP 2xx response - `unhealthy` -- Last check failed (error, timeout, or non-2xx response) - `unknown` -- App has not been checked yet --- ## Icons | Endpoint | Method | Role | Description | |----------|--------|------|-------------| | `/api/icons/dashboard` | GET | Any | List dashboard icons | | `/api/icons/dashboard/{name}` | GET | Any | Get dashboard icon metadata | | `/api/icons/lucide` | GET | Any | List Lucide icons | | `/api/icons/lucide/{name}` | GET | Any | Get Lucide icon SVG | | `/api/icons/custom` | GET | Any | List custom icons | | `/api/icons/custom` | POST | Admin | Upload custom icon (multipart, 2MB limit) | | `/api/icons/custom/fetch` | POST | Admin | Download icon from URL and save locally | | `/api/icons/custom/{name}` | DELETE | Admin | Delete custom icon | **Upload custom icon:** Send a multipart form request with the icon file: ``` POST /api/icons/custom Content-Type: multipart/form-data file: (binary data) ``` Accepted formats: PNG, SVG, JPG, WebP, GIF. Maximum file size: 2MB. **Fetch icon from URL:** Download an image from a remote URL and save it as a custom icon: ``` POST /api/icons/custom/fetch Content-Type: application/json { "url": "https://example.com/icon.png", "name": "my-icon" // optional, derived from URL if omitted } ``` The server downloads the image, validates the content type and size (2MB limit), and stores it locally. Returns `{"name": "my-icon", "status": "uploaded"}` on success. --- ## Themes | Endpoint | Method | Role | Description | |----------|--------|------|-------------| | `/api/themes` | GET | Any | List all themes | | `/api/themes` | POST | Admin | Save custom theme | | `/api/themes/{name}` | DELETE | Admin | Delete custom theme | --- ## Logs | Endpoint | Method | Auth Required | Description | |----------|--------|---------------|-------------| | `/api/logs/recent` | GET | Admin | Get recent log entries from the in-memory buffer (audit entries, client IPs, and panic stacks make this admin-only) | **Query parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `limit` | int | 200 | Maximum number of entries to return | | `level` | string | | Filter by log level (`debug`, `info`, `warn`, `error`) | | `source` | string | | Filter by source tag (`http`, `audit`, `server`, `auth`, `health`, `proxy`, `websocket`, `caddy`, `config`, `icons`, `themes`, `discovery`, `gateway`, `system`) | **Example:** ``` GET /api/logs/recent?limit=50&level=error ``` **Response:** ```json [ { "timestamp": "2026-01-01T12:00:00Z", "level": "INFO", "message": "HTTP request", "source": "http", "attrs": { "method": "GET", "path": "/api/config", "status": "200", "latency_ms": "12", "request_id": "a1b2c3d4e5f6a7b8" } } ] ``` Logs are stored in a 1000-entry ring buffer. When the buffer is full, the oldest entries are dropped. Entries are also broadcast in real-time via WebSocket (see below). --- ## System | Endpoint | Method | Auth Required | Description | |----------|--------|---------------|-------------| | `/api/system/info` | GET | Yes | Get system information (admin-only fields like data_dir, environment, and uptime are scrubbed for non-admin callers) | | `/api/system/updates` | GET | Admin | Check for available updates | **System info response:** ```json { "version": "3.0.0", "commit": "abc1234", "build_date": "2025-01-15T00:00:00Z", "go_version": "go1.26.0", "os": "linux", "arch": "amd64", "uptime": "2d 5h 30m", "environment": "docker" } ``` **Update check response:** ```json { "current_version": "3.1.1", "latest_version": "3.2.0", "update_available": true, "release_url": "https://github.com/mescon/Muximux/releases/tag/v3.2.0", "changelog": "..." } ``` --- ## Proxy Status | Endpoint | Method | Description | |----------|--------|-------------| | `/api/proxy/status` | GET | Get Caddy proxy status | **Response:** ```json { "enabled": true, "running": true, "tls": true, "domain": "muximux.example.com" } ``` --- ## Gateway The declarative `server.gateway_sites:` model is the current gateway path (the legacy Caddyfile was removed in 3.1.0). | Endpoint | Method | Role | Description | |----------|--------|------|-------------| | `/api/gateway/sites` | GET | Admin | List configured gateway sites | | `/api/gateway/sites` | POST | Admin | Create a gateway site | | `/api/gateway/sites/{domain}` | PUT | Admin | Update a gateway site | | `/api/gateway/sites/{domain}` | DELETE | Admin | Delete a gateway site | | `/api/gateway/validate` | POST | Admin | Validate a gateway site config without saving | `/api/auth/forward` is the internal forward-auth verify endpoint used by the gateway to gate access. It is not meant to be called directly from a browser (a direct hit returns `400`). --- ## WebSocket | Endpoint | Protocol | Description | |----------|----------|-------------| | `/ws` | WebSocket | Real-time event stream | Connect to `/ws` to receive real-time updates. Events are sent as JSON messages: ```json {"type": "config_updated", "payload": {...}} {"type": "health_changed", "payload": [...]} {"type": "app_health_changed", "payload": {"app": "Sonarr", "health": {"status": "healthy", ...}}} ``` **Event types:** | Type | Payload | Description | |------|---------|-------------| | `config_updated` | Full config object | Configuration was changed (via Settings panel or API) | | `health_changed` | Array of health statuses | Health status changed for one or more apps | | `app_health_changed` | `{"app": "name", "health": {...}}` | Health status changed for a specific app | | `docker_state_changed` | `{"app_name": "name", "state": {...}}` | Container state changed for a Docker-tracked app (status, health, restart count, etc.) | | `log_entry` | `LogEntry` object | A new log entry was recorded (see Logs section above). Admin-only: only broadcast to admin clients | The WebSocket client automatically reconnects if the connection drops, using exponential backoff (up to 30 seconds between retries, max 10 attempts). --- ## Rate Limiting The following endpoints are rate-limited to **5 attempts per IP address per minute** to prevent brute-force attacks. If the limit is exceeded, the endpoint returns HTTP 429 (Too Many Requests). - `POST /api/auth/login` -- Login attempts - `POST /api/auth/setup` -- Initial setup attempts - `GET /api/auth/oidc/login` -- OIDC login redirects (shares the login limiter) Other API endpoints are not rate-limited.