Chapter 5 of 6
Auto-Enrollment API Documentation
Overview
PatchMon's auto-enrollment API enables automated device onboarding using tools like Ansible, Terraform, or custom scripts. It covers token management, host enrollment, and agent installation endpoints.
Table of Contents
- API Architecture
- Authentication
- Admin Endpoints
- Enrollment Endpoints
- Host Management Endpoints
- Ansible Integration Examples
- Error Handling
- Rate Limiting
- Security Considerations
API Architecture
Base URL Structure
https://your-patchmon-server.com/api/v1/
The API version is v1 and is fixed in the server.
Endpoint Categories
| Category | Path Prefix | Authentication | Purpose |
|---|---|---|---|
| Admin | /auto-enrollment/tokens/* |
JWT (Bearer token) | Token management (CRUD) |
| Enrollment | /auto-enrollment/* |
Token key + secret (headers) | Host enrollment & script download |
| Host | /hosts/* |
API ID + key (headers) | Agent installation & data reporting |
Two-Tier Security Model
Tier 1: Auto-Enrollment Token
- Purpose: Create new host entries via enrollment
- Scope: Limited to enrollment operations only
- Authentication:
X-Auto-Enrollment-Key+X-Auto-Enrollment-Secretheaders - Rate Limited: Yes (configurable hosts per day per token)
- Storage: Secret is hashed (bcrypt) in the database
Tier 2: Host API Credentials
- Purpose: Agent communication (data reporting, updates, commands)
- Scope: Per-host unique credentials
- Authentication:
X-API-ID+X-API-KEYheaders - Rate Limited: No (per-host)
- Storage: API key is hashed (bcrypt) in the database
Why two tiers?
- Compromised enrollment token does not compromise existing hosts
- Compromised host credential does not compromise enrollment
- Revoking an enrollment token stops new enrollments without affecting existing hosts
Authentication
Admin Endpoints (JWT)
All admin endpoints require a valid JWT Bearer token from an authenticated user with "Manage Settings" permission:
curl -H "Authorization: Bearer <jwt_token>" \
-H "Content-Type: application/json" \
https://your-patchmon-server.com/api/v1/auto-enrollment/tokens
Enrollment Endpoints (Token Key + Secret)
Enrollment endpoints authenticate via custom headers:
curl -H "X-Auto-Enrollment-Key: patchmon_ae_abc123..." \
-H "X-Auto-Enrollment-Secret: def456ghi789..." \
-H "Content-Type: application/json" \
https://your-patchmon-server.com/api/v1/auto-enrollment/enroll
Host Endpoints (API ID + Key)
Host endpoints authenticate via API credential headers:
curl -H "X-API-ID: patchmon_abc123" \
-H "X-API-KEY: def456ghi789" \
https://your-patchmon-server.com/api/v1/hosts/install
Admin Endpoints
All admin endpoints require JWT authentication and "Manage Settings" permission.
Create Auto-Enrollment Token
Endpoint: POST /api/v1/auto-enrollment/tokens
Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
token_name |
string | Yes | (required) | Descriptive name (max 255 chars) |
max_hosts_per_day |
integer | No | 100 |
Rate limit (1–1000) |
default_host_group_id |
string | No | null |
UUID of host group to auto-assign |
allowed_ip_ranges |
string[] | No | [] |
IP whitelist (exact IPs or CIDR notation) |
expires_at |
string | No | null |
ISO 8601 expiration date |
metadata |
object | No | {} |
Custom metadata (e.g. integration_type, environment) |
scopes |
object | No | null |
Permission scopes (only for API integration type tokens) |
Example Request:
{
"token_name": "Proxmox Production",
"max_hosts_per_day": 100,
"default_host_group_id": "uuid-of-host-group",
"allowed_ip_ranges": ["192.168.1.10", "10.0.0.0/24"],
"expires_at": "2026-12-31T23:59:59Z",
"metadata": {
"integration_type": "proxmox-lxc",
"environment": "production"
}
}
Response: 201 Created
{
"message": "Auto-enrollment token created successfully",
"token": {
"id": "uuid",
"token_name": "Proxmox Production",
"token_key": "patchmon_ae_abc123...",
"token_secret": "def456ghi789...",
"max_hosts_per_day": 100,
"default_host_group": {
"id": "uuid",
"name": "Proxmox LXC",
"color": "#3B82F6"
},
"created_by": {
"id": "uuid",
"username": "admin",
"first_name": "John",
"last_name": "Doe"
},
"expires_at": "2026-12-31T23:59:59Z",
"scopes": null
},
"warning": "Save the token_secret now - it cannot be retrieved later!"
}
Important: The
token_secretis only returned in this response. It is hashed before storage and cannot be retrieved again.
List Auto-Enrollment Tokens
Endpoint: GET /api/v1/auto-enrollment/tokens
Response: 200 OK
[
{
"id": "uuid",
"token_name": "Proxmox Production",
"token_key": "patchmon_ae_abc123...",
"is_active": true,
"allowed_ip_ranges": ["192.168.1.10"],
"max_hosts_per_day": 100,
"hosts_created_today": 15,
"last_used_at": "2025-10-11T14:30:00Z",
"expires_at": "2026-12-31T23:59:59Z",
"created_at": "2025-10-01T10:00:00Z",
"default_host_group_id": "uuid",
"metadata": { "integration_type": "proxmox-lxc" },
"scopes": null,
"host_groups": {
"id": "uuid",
"name": "Proxmox LXC",
"color": "#3B82F6"
},
"users": {
"id": "uuid",
"username": "admin",
"first_name": "John",
"last_name": "Doe"
}
}
]
Tokens are returned in descending order by creation date. The token_secret is never included in list responses.
Get Token Details
Endpoint: GET /api/v1/auto-enrollment/tokens/{tokenId}
Response: 200 OK. Same structure as a single token in the list response (without token_secret).
Error: 404 Not Found if tokenId does not exist.
Update Token
Endpoint: PATCH /api/v1/auto-enrollment/tokens/{tokenId}
All fields are optional. Only include fields you want to change.
Request Body:
| Field | Type | Description |
|---|---|---|
token_name |
string | Updated name (1–255 chars) |
is_active |
boolean | Enable or disable the token |
max_hosts_per_day |
integer | Updated rate limit (1–1000) |
allowed_ip_ranges |
string[] | Updated IP whitelist |
default_host_group_id |
string | Updated host group (set to empty string to clear) |
expires_at |
string | Updated expiration date (ISO 8601) |
scopes |
object | Updated scopes (API integration type tokens only) |
Example Request:
{
"is_active": false,
"max_hosts_per_day": 200,
"allowed_ip_ranges": ["192.168.1.0/24"]
}
Response: 200 OK
{
"message": "Token updated successfully",
"token": {
"id": "uuid",
"token_name": "Proxmox Production",
"token_key": "patchmon_ae_abc123...",
"is_active": false,
"max_hosts_per_day": 200,
"allowed_ip_ranges": ["192.168.1.0/24"],
"host_groups": { "id": "uuid", "name": "Proxmox LXC", "color": "#3B82F6" },
"users": { "id": "uuid", "username": "admin", "first_name": "John", "last_name": "Doe" }
}
}
Errors:
404 Not Found: Token does not exist400 Bad Request: Host group not found, or scopes update attempted on a non-API token
Delete Token
Endpoint: DELETE /api/v1/auto-enrollment/tokens/{tokenId}
Response: 200 OK
{
"message": "Auto-enrollment token deleted successfully",
"deleted_token": {
"id": "uuid",
"token_name": "Proxmox Production"
}
}
Error: 404 Not Found if tokenId does not exist.
Enrollment Endpoints
Download Enrollment Script
Endpoint: GET /api/v1/auto-enrollment/script
This endpoint validates the token credentials, then serves a bash script with the PatchMon server URL, token credentials, and configuration injected automatically.
Query Parameters:
| Parameter | Required | Description |
|---|---|---|
type |
Yes | Script type: proxmox-lxc or direct-host |
token_key |
Yes | Auto-enrollment token key |
token_secret |
Yes | Auto-enrollment token secret |
force |
No | Set to true to enable force install mode (for broken packages) |
Example:
curl "https://patchmon.example.com/api/v1/auto-enrollment/script?type=proxmox-lxc&token_key=KEY&token_secret=SECRET"
Response: 200 OK. Plain text bash script with credentials injected.
Errors:
400 Bad Request: Missing or invalidtypeparameter401 Unauthorized: Missing credentials, invalid/inactive token, invalid secret, or expired token404 Not Found: Script file not found on server
Enroll Single Host
Endpoint: POST /api/v1/auto-enrollment/enroll
Headers:
X-Auto-Enrollment-Key: patchmon_ae_abc123...
X-Auto-Enrollment-Secret: def456ghi789...
Content-Type: application/json
Request Body:
| Field | Type | Required | Description |
|---|---|---|---|
friendly_name |
string | Yes | Display name for the host (max 255 chars) |
machine_id |
string | No | Unique machine identifier (max 255 chars) |
metadata |
object | No | Additional metadata (vmid, proxmox_node, ip_address, os_info, etc.) |
Example Request:
{
"friendly_name": "webserver",
"machine_id": "proxmox-lxc-100-abc123",
"metadata": {
"vmid": "100",
"proxmox_node": "proxmox01",
"ip_address": "10.0.0.10",
"os_info": "Ubuntu 22.04 LTS"
}
}
Response: 201 Created
{
"message": "Host enrolled successfully",
"host": {
"id": "uuid",
"friendly_name": "webserver",
"api_id": "patchmon_abc123def456",
"api_key": "raw-api-key-value",
"host_group": {
"id": "uuid",
"name": "Proxmox LXC",
"color": "#3B82F6"
},
"status": "pending"
}
}
Note: The
api_keyis only returned in this response (plain text). It is hashed before storage. Thehost_groupisnullif no default host group is configured on the token.
Error Responses:
| Status | Error | Cause |
|---|---|---|
400 |
Validation errors | Missing or invalid friendly_name |
401 |
Auto-enrollment credentials required |
Missing X-Auto-Enrollment-Key or X-Auto-Enrollment-Secret headers |
401 |
Invalid or inactive token |
Token key not found or token is disabled |
401 |
Invalid token secret |
Secret does not match |
401 |
Token expired |
Token has passed its expiration date |
403 |
IP address not authorized for this token |
Client IP not in allowed_ip_ranges |
429 |
Rate limit exceeded |
Token's max_hosts_per_day limit reached |
Duplicate handling: The API does not perform server-side duplicate host checks. Duplicate prevention is handled client-side by the enrollment script, which checks for an existing agent configuration (
/etc/patchmon/config.yml) inside each container before calling the API.
Host Management Endpoints
These endpoints are used by the PatchMon agent (not the enrollment script). They authenticate using the per-host X-API-ID and X-API-KEY credentials returned during enrollment.
Download Agent Installation Script
Endpoint: GET /api/v1/hosts/install
Serves a shell script that bootstraps the PatchMon agent on a host. The script uses a secure bootstrap token mechanism; actual API credentials are not embedded directly in the script.
Headers:
X-API-ID: patchmon_abc123
X-API-KEY: def456ghi789
Query Parameters:
| Parameter | Required | Description |
|---|---|---|
force |
No | Set to true to enable force install mode |
arch |
No | Architecture override (e.g. amd64, arm64); auto-detected if omitted |
Response: 200 OK. Plain text shell script with bootstrap token injected.
Download Agent Binary/Script
Endpoint: GET /api/v1/hosts/agent/download
Downloads the PatchMon agent binary (Go binary for modern agents) or migration script (for legacy bash agents).
Headers:
X-API-ID: patchmon_abc123
X-API-KEY: def456ghi789
Query Parameters:
| Parameter | Required | Description |
|---|---|---|
arch |
No | Architecture (e.g. amd64, arm64) |
force |
No | Set to binary to force binary download |
Response: 200 OK. Binary file or shell script.
Host Data Update
Endpoint: POST /api/v1/hosts/update
Used by the agent to report package data, system information, and hardware details.
Headers:
X-API-ID: patchmon_abc123
X-API-KEY: def456ghi789
Content-Type: application/json
Request Body Fields:
| Field | Type | Required | Description |
|---|---|---|---|
packages |
array | Yes | Array of package objects (max 10,000) |
packages[].name |
string | Yes | Package name |
packages[].currentVersion |
string | Yes | Currently installed version |
packages[].availableVersion |
string | No | Available update version |
packages[].needsUpdate |
boolean | Yes | Whether an update is available |
packages[].isSecurityUpdate |
boolean | No | Whether the update is security-related |
agentVersion |
string | No | Reporting agent version |
osType |
string | No | Operating system type |
osVersion |
string | No | Operating system version |
hostname |
string | No | System hostname |
ip |
string | No | System IP address |
architecture |
string | No | CPU architecture |
cpuModel |
string | No | CPU model name |
cpuCores |
integer | No | Number of CPU cores |
ramInstalled |
float | No | Installed RAM in GB |
swapSize |
float | No | Swap size in GB |
diskDetails |
array | No | Array of disk objects |
gatewayIp |
string | No | Default gateway IP |
dnsServers |
array | No | Array of DNS server IPs |
networkInterfaces |
array | No | Array of network interface objects |
kernelVersion |
string | No | Running kernel version |
installedKernelVersion |
string | No | Installed (on-disk) kernel version |
selinuxStatus |
string | No | SELinux status (enabled, disabled, or permissive) |
systemUptime |
string | No | System uptime. Inside a container (Proxmox LXC, Docker) this is the container's own uptime, not the host's |
bootTime |
string | No | Boot instant as a UTC ISO 8601 timestamp. Lets the UI tick uptime live between reports |
loadAverage |
array | No | Load average values |
machineId |
string | No | Machine ID |
needsReboot |
boolean | No | Whether a reboot is required |
rebootReason |
string | No | Reason a reboot is required |
repositories |
array | No | Configured package repositories |
executionTime |
string | No | Time taken to gather data |
Example Request:
{
"packages": [
{
"name": "nginx",
"currentVersion": "1.18.0",
"availableVersion": "1.20.0",
"needsUpdate": true,
"isSecurityUpdate": false
}
],
"agentVersion": "1.5.0",
"cpuModel": "Intel Xeon E5-2680 v4",
"cpuCores": 8,
"ramInstalled": 16.0,
"swapSize": 2.0,
"diskDetails": [
{
"device": "/dev/sda1",
"mountPoint": "/",
"size": "50GB",
"used": "25GB",
"available": "25GB"
}
],
"gatewayIp": "192.168.1.1",
"dnsServers": ["8.8.8.8", "8.8.4.4"],
"networkInterfaces": [
{
"name": "eth0",
"ip": "192.168.1.10",
"mac": "00:11:22:33:44:55"
}
],
"kernelVersion": "5.4.0-74-generic",
"selinuxStatus": "disabled"
}
Response: 200 OK
{
"message": "Host updated successfully",
"packagesProcessed": 1,
"updatesAvailable": 1,
"securityUpdates": 0
}
Ansible Integration Examples
Basic Playbook for Proxmox Enrollment
---
- name: Enroll Proxmox LXC containers in PatchMon
hosts: proxmox_hosts
become: yes
vars:
patchmon_url: "https://patchmon.example.com"
token_key: "{{ vault_patchmon_token_key }}"
token_secret: "{{ vault_patchmon_token_secret }}"
host_prefix: "prod-"
tasks:
- name: Install dependencies
apt:
name:
- curl
- jq
state: present
- name: Download enrollment script
get_url:
url: "{{ patchmon_url }}/api/v1/auto-enrollment/script?type=proxmox-lxc&token_key={{ token_key }}&token_secret={{ token_secret }}"
dest: /root/proxmox_auto_enroll.sh
mode: '0700'
- name: Run enrollment
command: /root/proxmox_auto_enroll.sh
environment:
HOST_PREFIX: "{{ host_prefix }}"
DEBUG: "true"
register: enrollment_output
- name: Show enrollment results
debug:
var: enrollment_output.stdout_lines
Advanced Playbook with Token Management
---
- name: Manage PatchMon Proxmox Integration
hosts: localhost
vars:
patchmon_url: "https://patchmon.example.com"
admin_token: "{{ vault_patchmon_admin_token }}"
tasks:
- name: Create Proxmox enrollment token
uri:
url: "{{ patchmon_url }}/api/v1/auto-enrollment/tokens"
method: POST
headers:
Authorization: "Bearer {{ admin_token }}"
Content-Type: "application/json"
body_format: json
body:
token_name: "{{ inventory_hostname }}-proxmox"
max_hosts_per_day: 200
default_host_group_id: "{{ proxmox_host_group_id }}"
allowed_ip_ranges: ["{{ proxmox_host_ip }}"]
expires_at: "2026-12-31T23:59:59Z"
metadata:
integration_type: "proxmox-lxc"
environment: "{{ environment }}"
status_code: 201
register: token_response
- name: Store token credentials
set_fact:
enrollment_token_key: "{{ token_response.json.token.token_key }}"
enrollment_token_secret: "{{ token_response.json.token.token_secret }}"
- name: Deploy enrollment script to Proxmox hosts
include_tasks: deploy_enrollment.yml
vars:
enrollment_token_key: "{{ enrollment_token_key }}"
enrollment_token_secret: "{{ enrollment_token_secret }}"
Ansible Role
# roles/patchmon_proxmox/tasks/main.yml
---
- name: Install PatchMon dependencies
package:
name:
- curl
- jq
state: present
- name: Create PatchMon directory
file:
path: /opt/patchmon
state: directory
mode: '0755'
- name: Download enrollment script
get_url:
url: "{{ patchmon_url }}/api/v1/auto-enrollment/script?type=proxmox-lxc&token_key={{ token_key }}&token_secret={{ token_secret }}&force={{ force_install | default('false') }}"
dest: /opt/patchmon/proxmox_auto_enroll.sh
mode: '0700'
- name: Run enrollment script
command: /opt/patchmon/proxmox_auto_enroll.sh
environment:
PATCHMON_URL: "{{ patchmon_url }}"
AUTO_ENROLLMENT_KEY: "{{ token_key }}"
AUTO_ENROLLMENT_SECRET: "{{ token_secret }}"
HOST_PREFIX: "{{ host_prefix | default('') }}"
DRY_RUN: "{{ dry_run | default('false') }}"
DEBUG: "{{ debug | default('false') }}"
FORCE_INSTALL: "{{ force_install | default('false') }}"
register: enrollment_output
- name: Display enrollment results
debug:
var: enrollment_output.stdout_lines
when: enrollment_output.stdout_lines is defined
- name: Fail if enrollment had errors
fail:
msg: "Enrollment failed with errors"
when: enrollment_output.rc != 0
Ansible Vault for Credentials
# group_vars/all/vault.yml (encrypted with ansible-vault)
---
vault_patchmon_admin_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
vault_patchmon_token_key: "patchmon_ae_abc123..."
vault_patchmon_token_secret: "def456ghi789..."
Playbook with Error Handling and Retries
---
- name: Robust Proxmox enrollment with error handling
hosts: proxmox_hosts
become: yes
vars:
patchmon_url: "https://patchmon.example.com"
token_key: "{{ vault_patchmon_token_key }}"
token_secret: "{{ vault_patchmon_token_secret }}"
max_retries: 3
retry_delay: 30
tasks:
- name: Test PatchMon connectivity
uri:
url: "{{ patchmon_url }}/api/v1/auto-enrollment/tokens"
method: GET
headers:
Authorization: "Bearer {{ vault_patchmon_admin_token }}"
status_code: 200
retries: "{{ max_retries }}"
delay: "{{ retry_delay }}"
- name: Download enrollment script
get_url:
url: "{{ patchmon_url }}/api/v1/auto-enrollment/script?type=proxmox-lxc&token_key={{ token_key }}&token_secret={{ token_secret }}"
dest: /root/proxmox_auto_enroll.sh
mode: '0700'
retries: "{{ max_retries }}"
delay: "{{ retry_delay }}"
- name: Run enrollment with retry logic
shell: |
for i in {1..{{ max_retries }}}; do
echo "Attempt $i of {{ max_retries }}"
if /root/proxmox_auto_enroll.sh; then
echo "Enrollment successful"
exit 0
else
echo "Enrollment failed, retrying in {{ retry_delay }} seconds..."
sleep {{ retry_delay }}
fi
done
echo "All enrollment attempts failed"
exit 1
register: enrollment_result
- name: Handle enrollment failure
fail:
msg: "Proxmox enrollment failed after {{ max_retries }} attempts"
when: enrollment_result.rc != 0
- name: Parse enrollment results
set_fact:
enrolled_count: "{{ enrollment_result.stdout | regex_search('Successfully Enrolled:\\s+(\\d+)', '\\1') | default('0') }}"
failed_count: "{{ enrollment_result.stdout | regex_search('Failed:\\s+(\\d+)', '\\1') | default('0') }}"
- name: Report enrollment statistics
debug:
msg: |
Enrollment completed:
- Successfully enrolled: {{ enrolled_count }} containers
- Failed: {{ failed_count }} containers
Error Handling
HTTP Status Codes
| Code | Meaning | When It Occurs |
|---|---|---|
200 |
OK | Successful read/update operations |
201 |
Created | Token or host created successfully |
400 |
Bad Request | Validation errors, invalid host group, invalid script type |
401 |
Unauthorized | Missing, invalid, or expired credentials |
403 |
Forbidden | IP address not in token's whitelist |
404 |
Not Found | Token or resource not found |
429 |
Too Many Requests | Token's daily host creation limit exceeded |
500 |
Internal Server Error | Unexpected server error |
Error Response Formats
Simple error:
{
"error": "Error message describing what went wrong"
}
Error with detail:
{
"error": "Rate limit exceeded",
"message": "Maximum 100 hosts per day allowed for this token"
}
Validation errors (400):
{
"errors": [
{
"msg": "Token name is required (max 255 characters)",
"param": "token_name",
"location": "body"
}
]
}
Rate Limiting
Token-Based Rate Limits
Each auto-enrollment token has a configurable max_hosts_per_day limit:
- Default: 100 hosts per day per token
- Range: 1–1000 hosts per day
- Reset: Daily (when the first request of a new day is received)
- Scope: Per-token, not per-IP
When the limit is exceeded, the API returns 429 Too Many Requests:
{
"error": "Rate limit exceeded",
"message": "Maximum 100 hosts per day allowed for this token"
}
Global Rate Limiting
The auto-enrollment endpoints are also subject to the server's global authentication rate limiter, which applies to all authentication-related endpoints.
Security Considerations
Token Security
- Secret hashing: Token secrets are hashed with bcrypt (cost factor 10) before storage
- One-time display: Secrets are only returned during token creation
- Rotation: Recommended every 90 days
- Scope limitation: Tokens can only create hosts. They cannot read, modify, or delete existing host data.
IP Restrictions
Tokens support IP whitelisting with both exact IPs and CIDR notation:
{
"allowed_ip_ranges": ["192.168.1.10", "10.0.0.0/24"]
}
IPv4-mapped IPv6 addresses (e.g. ::ffff:192.168.1.10) are automatically handled.
Host API Key Security
- Host API keys (
api_key) are hashed with bcrypt before storage - The installation script uses a bootstrap token mechanism; the actual API credentials are not embedded in the script
- Bootstrap tokens are single-use and expire after 5 minutes
Network Security
- Always use HTTPS in production
- The
ignore_ssl_self_signedserver setting automatically configures curl flags in served scripts - Implement firewall rules to restrict PatchMon server access to known IPs
Audit Trail
All enrollment activity is logged:
- Token name included in host notes (e.g. "Auto-enrolled via Production Proxmox on 2025-10-11T14:30:00Z")
- Token creation tracks
created_by_user_id last_used_attimestamp updated on each enrollment
Complete Endpoint Summary
Admin Endpoints (JWT Authentication)
| Method | Path | Description |
|---|---|---|
POST |
/api/v1/auto-enrollment/tokens |
Create token |
GET |
/api/v1/auto-enrollment/tokens |
List all tokens |
GET |
/api/v1/auto-enrollment/tokens/{tokenId} |
Get single token |
PATCH |
/api/v1/auto-enrollment/tokens/{tokenId} |
Update token |
DELETE |
/api/v1/auto-enrollment/tokens/{tokenId} |
Delete token |
Enrollment Endpoints (Token Authentication)
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/auto-enrollment/script?type=... |
Download enrollment script |
POST |
/api/v1/auto-enrollment/enroll |
Enroll a host |
Host Endpoints (API Credentials)
| Method | Path | Description |
|---|---|---|
GET |
/api/v1/hosts/install |
Download installation script |
GET |
/api/v1/hosts/agent/download |
Download agent binary/script |
POST |
/api/v1/hosts/update |
Report host data |
Quick Reference: curl Examples
Create a token:
curl -X POST \
-H "Authorization: Bearer <jwt_token>" \
-H "Content-Type: application/json" \
-d '{
"token_name": "Production Proxmox",
"max_hosts_per_day": 100,
"default_host_group_id": "uuid",
"allowed_ip_ranges": ["192.168.1.10"]
}' \
https://patchmon.example.com/api/v1/auto-enrollment/tokens
Download and run enrollment script:
curl -s "https://patchmon.example.com/api/v1/auto-enrollment/script?type=proxmox-lxc&token_key=KEY&token_secret=SECRET" | bash
Enroll a host directly:
curl -X POST \
-H "X-Auto-Enrollment-Key: patchmon_ae_abc123..." \
-H "X-Auto-Enrollment-Secret: def456ghi789..." \
-H "Content-Type: application/json" \
-d '{
"friendly_name": "webserver",
"machine_id": "proxmox-lxc-100-abc123"
}' \
https://patchmon.example.com/api/v1/auto-enrollment/enroll
Download agent installation script:
curl -H "X-API-ID: patchmon_abc123" \
-H "X-API-KEY: def456ghi789" \
https://patchmon.example.com/api/v1/hosts/install | bash
Integration Patterns
Pattern 1: Script-Based (Simplest)
# Download and execute in one command (credentials are injected into the script)
curl -s "https://patchmon.example.com/api/v1/auto-enrollment/script?type=proxmox-lxc&token_key=KEY&token_secret=SECRET" | bash
Pattern 2: API-First (Most Control)
# 1. Create token via admin API
# 2. Enroll hosts via enrollment API
# 3. Download agent scripts using per-host API credentials
# 4. Install agents with host-specific credentials
Pattern 3: Hybrid (Recommended for Automation)
# 1. Create token via admin API (or UI)
# 2. Download enrollment script with token embedded
# 3. Distribute and run script on Proxmox hosts
# 4. Script handles both enrollment and agent installation