Auto-enrolment api docs
Auto-Enrollment API Documentation
Overview
This document provides comprehensive API documentation for PatchMon's Proxmox integration, focusing on how to leverage the auto-enrollment APIsystem, endpointscovering fortoken management, host enrollment, and agent installation endpoints. These APIs enable automated device managementonboarding using tools like Ansible.
TheTerraform, PatchMonor Proxmoxcustom integration provides a secure, token-based API for automatically enrolling LXC containers from Proxmox hosts into PatchMon for centralized patch management.scripts.
Table of Contents
- API Architecture
- Authentication
- Admin Endpoints
- Enrollment Endpoints
- Host Management Endpoints
- Ansible Integration Examples
- Error Handling
- Rate Limiting
- Security Considerations
API Reference
API Architecture
Base URL Structure
https://your-patchmon-server.com/api/v1/
Note: The API version is configurable via the API_VERSION environment variable (defaults to v1).
Endpoint Categories
-
Category Path Prefix Authentication Purpose Admin Endpoints(/auto-enrollment/tokens/*)-JWT (Bearer token) Token management - (CRUD)
Enrollment Endpoints(/auto-enrollment/*)-Token key + secret (headers) Host enrollment - & script download
Host Endpoints(/hosts/*)-API HostIDmanagement+andkeyagent(headers)Agent installation & data reporting Purpose:Purpose: Create new host entries via enrollmentScope:Scope: Limited to enrollment operations onlyAuthentication:Authentication:Token keyX-Auto-Enrollment-Key+secretX-Auto-Enrollment-Secretheaders- Rate
Limited:Limited: Yes (configurable hosts perday)day per token) - Storage: Secret is hashed (bcrypt) in the database
Purpose:Purpose: Agent communicationand(datareportingreporting, updates, commands)Scope:Scope: Per-host unique credentialsAuthentication:Authentication:APIX-API-ID+keyX-API-KEYheaders- Rate
Limited:Limited: No (per-host) - Storage: API key is hashed (bcrypt) in the database
- Compromised enrollment token ≠ compromised hosts
- Compromised host credential ≠ compromised enrollment
- Revoking an enrollment token stops new enrollments without affecting existing hosts
404 Not Found— Token does not exist400 Bad Request— Host group not found, or scopes update attempted on a non-API token-
Parameter Required Description typeYes Script type: proxmox-lxcordirect-hosttoken_key(required):Yes Auto-enrollment token key token_secret(required):Yes Auto-enrollment token secret force(optional):No Set to trueto enable force install mode (for broken packages) 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- Minimum: 1 host per request
- Maximum: 50 hosts per request
- Each host must have a
friendly_name(required);machine_idis optional -
Parameter Required Description force(optional):No Set to trueto enable force install modearchNo Architecture override (e.g. amd64,arm64); auto-detected if omitted Defaultdetail:: 100 hosts per day per tokenMaximum: 1000 hosts per day per tokenReset: Daily at midnight UTCScope: Per-token (not per-host)- 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
- Secret
Storagehashing: Token secrets are hashedusingwith bcrypt (cost factor 10) before storage - One-
TimetimeDisplaydisplay: Secrets are onlyshownreturned during token creation - Rotation:
RegularRecommendedtokeneveryrotation recommended (90days)days - Scope limitation: Tokens can only create
hosts,hosts — they cannotaccessread, modify, or delete existing host data - 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
UseAlways use HTTPS in production- The
ignore_ssl_self_signedserver setting automatically configures curl flags in served scripts - Implement
properfirewall rules ConsidertoVPNrestrictforPatchMonremoteserverProxmoxaccesshoststo Monitorknownfor unusual enrollment patternsIPs- Token name
andincludedIDin host notes (e.g. "Auto-enrolled via Production Proxmox on 2025-10-11T14:30:00Z") EnrolledTokenhostcreationdetailstrackscreated_by_user_idTimestamplast_used_atandtimestampsourceupdatedIPon Success/failureeachstatusenrollment-
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 detailsPATCH/api/v1/auto-enrollment/tokens/{tokenId}-Update token DELETE/api/v1/auto-enrollment/tokens/{tokenId}-Delete token -
Method Path Description GET/api/v1/auto-enrollment/proxmox-lxcscript?type=...-Download enrollment script POST/api/v1/auto-enrollment/enroll-Enroll single host POST/api/v1/auto-enrollment/enroll/bulk-Bulk enroll hosts (max 50)
-
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- UpdateReport host data
Two-Tier Security Model
Tier 1: Auto-Enrollment Token
Tier 2: Host API Credentials
Why two tiers?
Authentication
Admin Endpoints Authentication(JWT)
AdminAll admin endpoints require a valid JWT authentication: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 Authentication(Token Key + Secret)
Enrollment endpoints useauthenticate token-basedvia authentication: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 Authentication(API ID + Key)
Host endpoints useauthenticate via API credentials: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
Headers:Request Body:
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
|
string | Yes | — | 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) |
RequestExample Body: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",
"description": "Token for production Proxmox cluster"
}
}
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— Same structure as a single token in list)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"],
"expires_at": "2027-01-01T00:00:00Z"
}
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:
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 Proxmox Enrollment Script
Endpoint: GET /api/v1/auto-enrollment/proxmox-lxcscript
This endpoint validates the token credentials, then serves a bash script with the PatchMon server URL, token credentials, and configuration injected automatically.
Query Parameters:
Example:
curl "https://patchmon.example.com/api/v1/auto-enrollment/script?type=proxmox-lxc?lxc&token_key=KEY&token_secret=SECRET&force=true"SECRET"
Response: 200 OK (— Plain text bash script with credentials injected)injected.
Errors:
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",
"container_type": "lxc"
}
}
Response: 201 Created
{
"message": "Host enrolled successfully",
"host": {
"id": "uuid",
"friendly_name": "webserver",
"api_id": "patchmon_abc123"patchmon_abc123def456",
"api_key": "def456ghi789"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 |
Missing or invalid |
401 |
|
Missing
|
401 |
Invalid or inactive token |
Token key not or token is disabled |
|
Invalid |
Secret does not match |
401 |
Token expired |
Token has passed its expiration date |
403 |
IP address not authorized for this |
Client IP
|
429 |
Rate limit |
Token's limit |
perDuplicate
dayhandling:allowedThe API does not perform server-side duplicate host checks. Duplicate prevention is handled client-side by the enrollment script, which checks forthisantoken"existing}agent configuration (/etc/patchmon/config.yml) inside each container before calling the API.
Bulk Enroll Hosts
Endpoint: POST /api/v1/auto-enrollment/enroll/bulk
Headers:
X-Auto-Enrollment-Key: patchmon_ae_abc123...
X-Auto-Enrollment-Secret: def456ghi789...
Content-Type: application/json
Request Body:
{
"hosts": [
{
"friendly_name": "webserver",
"machine_id": "proxmox-lxc-100-abc123",
"metadata": {
"vmid": "100",
"proxmox_node": "proxmox01"
}
},
{
"friendly_name": "database",
"machine_id": "proxmox-lxc-101-def456",
"metadata": {
"vmid": "101",
"proxmox_node": "proxmox01"
}
}
]
}
Limits:
Response: 201 Created
{
"message": "Bulk enrollment completed: 2 succeeded, 0 failed, 0 skipped",
"results": {
"success": [
{
"id": "uuid",
"friendly_name": "webserver",
"api_id": "patchmon_abc123",
"api_key": "def456"
},
{
"id": "uuid",
"friendly_name": "database",
"api_id": "patchmon_ghi789",
"api_key": "jkl012"
}
],
"failed": [],
"skipped": []
}
}
Rate Limit Error (429):
{
"error": "Rate limit exceeded",
"message": "Only 5 hosts remaining in daily quota"
}
The bulk endpoint checks the remaining daily quota before processing. If the number of hosts in the request exceeds the remaining quota, the entire request is rejected.
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:
Response: 200 OK (bash— Plain text shell script with credentialsbootstrap injected)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 (agent— scriptBinary withfile credentialsor injected)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: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 |
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.2.3",
"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 Ansible 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?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 Ansible 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 }}"
created_by_ansible: true
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 TaskPlaybook for Bulk Enrollment via API
---
- name: Bulk enroll Proxmox containers
hosts: proxmox_hosts
become: yes
vars:
patchmon_url: "https://patchmon.example.com"
token_key: "{{ vault_patchmon_token_key }}"
token_secret: "{{ vault_patchmon_token_secret }}"
tasks:
- name: Get LXC container list
shell: |
pct list | tail -n +2 | while read -r line; do
vmid=$(echo "$line" | awk '{print $1}')
name=$(echo "$line" | awk '{print $3}')
status=$(echo "$line" | awk '{print $2}')
if [ "$status" = "running" ]; then
machine_id=$(pct exec "$vmid" -- bash -c "cat /etc/machine-id 2>/dev/null || cat /var/lib/dbus/machine-id 2>/dev/null || echo 'proxmox-lxc-$vmid-'$(cat /proc/sys/kernel/random/uuid)" 2>/dev/null || echo "proxmox-lxc-$vmid-unknown")
echo "{\"friendly_name\":\"$name\",\"machine_id\":\"$machine_id\",\"metadata\":{\"vmid\":\"$vmid\",\"proxmox_node\":\"$(hostname)\"}}"
fi
done | jq -s '.'
register: containers_json
- name: Bulk enroll containers
uri:
url: "{{ patchmon_url }}/api/v1/auto-enrollment/enroll/bulk"
method: POST
headers:
X-Auto-Enrollment-Key: "{{ token_key }}"
X-Auto-Enrollment-Secret: "{{ token_secret }}"
Content-Type: "application/json"
body_format: json
body:
hosts: "{{ containers_json.stdout | from_json }}"
status_code: 201
register: enrollment_result
- name: Display enrollment results
debug:
msg: "{{ enrollment_result.json.message }}"
Ansible Role for PatchMon Integration
# 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?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 Integrationfor 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..."
Ansible 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?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
Common HTTP Status Codes
| Code | Meaning | |
|---|---|---|
200 |
OK | |
201 |
Created | |
400 |
Bad Request | |
401 |
Unauthorized | |
403 |
Forbidden | IP address not |
404 |
Not Found | |
429 |
Too Many Requests | |
500 |
Internal Server Error |
Error Response FormatFormats
Simple error:
{
"error": "Error message",message "message":describing "Detailedwhat description",went "code": "ERROR_CODE",
"details": {
"field": "additional context"
}wrong"
}
Validation
Error Errorswith {
"errors": [
{
"msg": "Token name is required",
"param": "token_name",
"location": "body"
}
]
}
Rate Limiting
Token Rate Limits
Rate Limit Headers
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 85
X-RateLimit-Reset: 1640995200
Rate Limit Exceeded Response
{
"error": "Rate limit exceeded",
"message": "Maximum 100 hosts per day allowed for this token",
"retry_after": 3600
}
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:
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"
}
For bulk enrollment, the remaining daily quota is checked against the request size. If the request contains more hosts than the remaining quota allows, the entire request is rejected:
{
"error": "Rate limit exceeded",
"message": "Only 5 hosts remaining in daily quota"
}
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
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
Network Security
Audit LoggingTrail
All enrollment activitiesactivity areis logged with:logged:
API Reference
Complete Endpoint List
Summary
Admin Endpoints (JWT Authentication)
Enrollment Endpoints (Token Authentication)
Host Endpoints (API Credentials)
Request/ResponseQuick Reference: curl Examples
Create Tokena Requesttoken:
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"],
"expires_at": "2026-12-31T23:59:59Z"
}' \
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 Hosta Requesthost 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",
"metadata": {
"vmid": "100",
"proxmox_node": "proxmox01"
}
}' \
https://patchmon.example.com/api/v1/auto-enrollment/enroll
Download Installationagent Scriptinstallation 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: DirectScript-Based Script Download(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?lxc&token_key=KEY&token_secret=SECRET" | bash
Pattern 2: API-First Approach(Most Control)
# 1. Create token via admin API
# 2. Use token to enrollEnroll hosts via enrollment API (single or bulk)
# 3. Download agent scripts forusing eachper-host hostAPI credentials
# 4. Install agents usingwith host-specific credentials
Pattern 3: Hybrid Approach(Recommended for Automation)
# 1. Create token via admin API (or UI)
# 2. Download enrollment script with token embedded
# 3. RunDistribute and run script on Proxmox hosts
# 4. Script handles both enrollment and agent installation
This API documentation provides everything needed to integrate PatchMon's Proxmox functionality with automation tools like Ansible. The endpoints are designed to be secure, scalable, and easy to integrate into existing infrastructure automation workflows.