Deployment Guide for Hosters
VM deployment guide for hosters and platform resellers — how the reseller-to-deployment-to-Sensor token chain works, and how to integrate Imunify Sensor into your VM provisioning flow.
This guide explains how Hosters and Platform Resellers integrate Imunify Sensor deployment into their VM provisioning flow.
Imunify Sensor is installed inside customer VMs to protect AI agent runtimes. The integration is designed so VM images and templates never contain long-lived Imunify for AI agents credentials. A VM receives only a short-lived, one-time deployment token during provisioning.
Who This Guide Is For#
There are two common real-world models.
Model A: Hoster Directly Provisions Customer VMs#
This is the most common model.
The Hoster sells VMs or managed AI agent environments directly to customers. The Hoster owns the provisioning system, VM templates, first-boot scripts, and customer lifecycle. In Imunify for AI agents, the Hoster belongs to a reseller tenant and uses that tenant to create deployment sessions for its own customer VMs.
In this model:
- the Hoster receives or manages the reseller tenant credentials
- the Hoster creates an internal provisioning token for its own automation
- the Hoster provisioning system requests one deployment token per VM
- the VM receives only the one-time deployment token
- the VM bootstrap installs Imunify Sensor during first boot or provisioning
Model B: Platform Reseller Delegates VM Provisioning to Partners#
This model is used when the Imunify for AI agents customer operates a hosting platform, virtualization platform, marketplace, or provisioning layer used by downstream Partners.
The Platform Reseller usually does not provision each end-user VM directly. Instead, Partners sell or operate the VMs for their own customers. The Platform Reseller owns the Imunify for AI agents reseller tenant and issues scoped provisioning tokens to Partners.
In this model:
- the Platform Reseller receives or manages the reseller tenant credentials
- the Platform Reseller creates one scoped provisioning token per Partner
- the Partner stores its provisioning token in its own backend
- the Partner requests one deployment token per VM
- the Partner injects the deployment token into its VM template or first-boot provisioning flow
- the VM receives only the one-time deployment token
Shared Deployment Principle#
Both models use the same token flow:
reseller token -> provisioning token -> deployment token -> Sensor token
Only the deployment token reaches the VM. Reseller and provisioning tokens stay in backend systems.
Token Types#
| Token | Example | Owner | Used by | Purpose | Lifetime | Put in VM image? |
|---|---|---|---|---|---|---|
| Reseller backend token | sk-reseller-* | Hoster or Platform Reseller tenant | Tenant backend | Manage tenant namespace, create users, create provisioning tokens, create deployments | Until revoked/rotated | No |
| Provisioning token | sk-deployer-* | Hoster automation or Partner automation | Provisioning backend | Create/read/cancel deployment sessions within scoped limits | Until revoked/rotated | No |
| Deployment token | depbt_* | One VM deployment attempt | VM bootstrap script | One-time token exchanged for Sensor token and installer config | Default 1 hour | Inject at provisioning time only |
| Sensor token | sk-sensor-* | Installed Sensor | Sensor service | Authenticates Sensor to Imunify for AI agents | Until revoked/rotated | No; bootstrap receives it during claim |
| Report token | deprt_* | VM bootstrap | Bootstrap report step | Reports install progress/failure after claim | Default 30 minutes | No; bootstrap receives it during claim |
sk-reseller-* and sk-deployer-* are backend credentials. Treat them as
secrets. depbt_* is short-lived and single-use. It is the only token that
should be passed to a VM.
Default lifetimes are deployment settings:
depbt_*deployment token:token_lifetime_seconds, default3600seconds.deprt_*report token:report_token_lifetime_seconds, default1800seconds.- claimed-but-never-registered cleanup:
stale_claimed_after_seconds, default1800seconds.
Component Relationship#
flowchart LR
subgraph Imunify["Imunify for AI agents"]
API["Reseller API"]
InstallAPI["Install Bootstrap + Claim API"]
Panel["Panel / Stats / Events"]
end
subgraph Tenant["Hoster or Platform Reseller Tenant"]
Backend["Tenant backend"]
TenantProvisioning["Tenant provisioning system"]
end
subgraph Partner["Partner Infrastructure (Model B only)"]
PartnerBackend["Partner provisioning backend"]
end
subgraph VM["Customer VM"]
FirstBoot["VM first-boot script"]
Sensor["Imunify Sensor"]
Runtime["AI agent runtime"]
end
Backend -- "sk-reseller-*" --> API
Backend -- "creates sk-deployer-*" --> API
TenantProvisioning -- "sk-deployer-* (Model A)" --> API
PartnerBackend -- "sk-deployer-* (Model B)" --> API
TenantProvisioning -- "depbt_*" --> FirstBoot
PartnerBackend -- "depbt_*" --> FirstBoot
FirstBoot -- "claim depbt_*" --> InstallAPI
FirstBoot -- "verified installer" --> Sensor
Sensor -- "events / heartbeats" --> Imunify
Sensor -- "protects" --> Runtime
Panel -- "stats / events" --> Imunify
Token Sequence#
sequenceDiagram
participant I as Imunify for AI agents API
participant T as Hoster or Platform Reseller backend
participant P as Hoster/Partner provisioning backend
participant V as Customer VM
participant S as Imunify Sensor
I->>T: Issue sk-reseller-* out of band
T->>I: POST /api/v1/reseller/provisioning-tokens
I-->>T: Return sk-deployer-* once
P->>I: POST /api/v1/reseller/sensor-deployments using sk-deployer-*
I-->>P: Return depbt_* and bootstrap command
P->>V: Inject depbt_* into VM provisioning data
V->>I: POST /api/v1/install/deployments/claim with depbt_*
I-->>V: Return sk-sensor-*, installer URL, checksum URL, config
V->>I: Download installer and checksum
V->>V: Verify SHA256
V->>S: Install and start Sensor
S->>I: Register and send heartbeats/events
Model A: Hoster Directly Provisions Customer VMs#
Use this model when your company sells VMs or managed AI agent environments directly to customers and controls the VM provisioning workflow.
Responsibilities#
The Hoster:
- stores
sk-reseller-*only in its backend secrets store - creates an internal
sk-deployer-*for VM provisioning automation - creates one deployment session per VM
- injects the returned
depbt_*into the VM at provisioning time - installs Imunify Sensor during first boot or image customization
- monitors deployments, Sensor status, protected runtime count, and events
1. Create an Internal Provisioning Token#
This call is made from the Hoster backend.
curl -sS -X POST "https://panel.imunify.ai/api/v1/reseller/provisioning-tokens" \
-H "Authorization: Bearer sk-reseller-..." \
-H "Content-Type: application/json" \
-d '{
"label": "main VM provisioning",
"scopes": [
"sensor_deployments:create",
"sensor_deployments:read",
"sensor_deployments:cancel"
],
"allowed_external_user_prefixes": ["customer-"],
"allowed_host_external_id_prefixes": ["vm-"],
"rate_limit_per_minute": 120,
"rate_limit_per_hour": 5000
}'
The response includes the raw sk-deployer-* once. Store it in the Hoster
provisioning backend secret store.
2. Request a Deployment Token During VM Creation#
This call is made by the Hoster provisioning system while creating a VM.
curl -sS -X POST "https://panel.imunify.ai/api/v1/reseller/sensor-deployments" \
-H "Authorization: Bearer sk-deployer-..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: vm-create-vm-12345" \
-d '{
"external_user_id": "customer-987",
"external_user_display_name": "Customer 987",
"host_external_id": "vm-12345",
"label": "customer-987-vm-12345",
"target_processes": ["ai-agent-runtime"],
"metadata": {
"template": "linux-ai-agent-template",
"plan": "ai-agent-protected"
}
}'
The response includes a one-time deployment token and a ready-to-run bootstrap command:
{
"deployment_id": "dep_...",
"deployment_token": "depbt_...",
"expires_at": "2026-05-25T11:00:00+00:00",
"bootstrap_command": "curl -fsSL https://panel.imunify.ai/api/v1/install/bootstrap.sh | sudo bash -s -- --deployment-token depbt_...",
"status_url": "/api/v1/reseller/sensor-deployments/dep_..."
}
Use deployment_id for later status checks, rotation, and cancellation. See
How do I check the status of one VM deployment?
for the full status example.
Use expires_at to make sure the VM claims the deployment token before it
expires.
3. Inject the Deployment Token Into the VM#
Example cloud-init:
#cloud-config
runcmd:
- |
set -euo pipefail
DEPLOYMENT_TOKEN="${IMUNIFY_DEPLOYMENT_TOKEN}"
curl -fsSL https://panel.imunify.ai/api/v1/install/bootstrap.sh \
| sudo bash -s -- --deployment-token "${DEPLOYMENT_TOKEN}"
The provisioning system replaces ${IMUNIFY_DEPLOYMENT_TOKEN} with the fresh
depbt_* for this VM.
Model B: Platform Reseller With Partners#
Use this model when your company operates a platform and downstream Partners provision or sell VMs to their own customers.
Responsibilities#
The Platform Reseller:
- stores
sk-reseller-*only in its backend secrets store - creates one scoped
sk-deployer-*per Partner - restricts each Partner token with
partner_id, ID prefixes, scopes, and rate limits - monitors Partner deployment usage, protected runtime count, and events
- revokes or rotates Partner provisioning tokens when needed
The Partner:
- stores its
sk-deployer-*only in its own provisioning backend - creates one deployment session per VM
- injects the returned
depbt_*into the VM at provisioning time - installs Imunify Sensor during first boot or image customization
- never receives
sk-reseller-*
1. Platform Reseller Creates a Partner Provisioning Token#
This call is made from the Platform Reseller backend.
curl -sS -X POST "https://panel.imunify.ai/api/v1/reseller/provisioning-tokens" \
-H "Authorization: Bearer sk-reseller-..." \
-H "Content-Type: application/json" \
-d '{
"partner_id": "partner-a",
"label": "Partner provisioning",
"scopes": [
"sensor_deployments:create",
"sensor_deployments:read",
"sensor_deployments:cancel"
],
"allowed_external_user_prefixes": ["partner-a-customer-"],
"allowed_host_external_id_prefixes": ["partner-a-vm-"],
"rate_limit_per_minute": 60,
"rate_limit_per_hour": 1000,
"metadata": {
"owner": "partner-a"
}
}'
The response includes the raw sk-deployer-* once. Give it to the Partner
through a secure onboarding process. The Partner stores it in its provisioning
backend secret store.
If a Partner cannot see or manage a deployment later, check the scope rules in
What if the Partner cannot access a deployment?.
2. Partner Requests a Deployment Token Per VM#
This call is made by the Partner provisioning backend while creating a VM.
curl -sS -X POST "https://panel.imunify.ai/api/v1/reseller/sensor-deployments" \
-H "Authorization: Bearer sk-deployer-..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: vm-create-partner-a-vm-12345" \
-d '{
"external_user_id": "partner-a-customer-987",
"external_user_display_name": "Customer 987",
"host_external_id": "partner-a-vm-12345",
"label": "customer-987-vm-12345",
"partner_id": "partner-a",
"target_processes": ["ai-agent-runtime"],
"metadata": {
"template": "linux-ai-agent-template",
"plan": "ai-agent-protected"
}
}'
The response includes depbt_* and bootstrap_command. The Partner injects
depbt_* into the VM provisioning data. The VM does not need to know whether
the deployment came from Model A or Model B.
Use the returned deployment_id for Partner-side status checks, rotation, and
cancellation. See
How do I check the status of one VM deployment?.
VM Bootstrap Options#
The VM bootstrap step can use either:
- Imunify for AI agents’s ready-made bootstrap script:
https://panel.imunify.ai/api/v1/install/bootstrap.sh - a custom bootstrap script maintained by the Hoster or Partner
The ready-made script is recommended unless the provisioning system needs custom logging, proxy setup, OS preparation, or tighter integration with an existing template framework.
Minimal VM Bootstrap Script#
This script belongs in a VM template. The token value must be supplied dynamically by the provisioning layer.
#!/usr/bin/env bash
set -euo pipefail
: "${IMUNIFY_DEPLOYMENT_TOKEN:?missing IMUNIFY_DEPLOYMENT_TOKEN}"
case "$IMUNIFY_DEPLOYMENT_TOKEN" in
depbt_*) ;;
*)
echo "Expected depbt_* deployment token" >&2
exit 2
;;
esac
curl -fsSL https://panel.imunify.ai/api/v1/install/bootstrap.sh \
| sudo bash -s -- --deployment-token "$IMUNIFY_DEPLOYMENT_TOKEN"
Bootstrap Script Responsibilities#
Whether you use the Imunify for AI agents bootstrap script or a custom script, the bootstrap must perform the same security-sensitive flow:
- Validate that the provided token is a
depbt_*deployment token, not a reseller, provisioning, user, admin, or Sensor API token. - Claim
depbt_*via/api/v1/install/deployments/claim. - Receive
server_url,sk-sensor-*, installer URL, checksum URL, label, report token, and target processes. - Download the Sensor installer.
- Download the installer
.sha256. - Verify the installer SHA256 before execution.
- Run the verified installer with
IMUNIFYAI_*environment variables. - Optionally report install progress/failure with the returned
deprt_*report token.
The bootstrap script is responsible for safe download and handoff. The installer is responsible for changing the VM.
What the Sensor Installer Does#
The Sensor installer is the host-side setup script that performs the actual installation work:
- Validates host prerequisites such as Linux support, required tools, BTF, and active eBPF LSM support.
- Installs the Imunify Sensor binary and configuration.
- Writes the Sensor token and server URL received from the deployment claim.
- Installs and registers the AI runtime integration plugin when that integration is enabled for the deployment profile.
- Installs systemd units for the Sensor and upgrade/rollback helpers.
- Starts or restarts the required services.
- Leaves the Sensor running so it can register with Imunify for AI agents and begin sending heartbeats/events.
The bootstrap script does not embed reseller credentials. It only uses the
one-time depbt_* token and exchanges it for a VM-specific sk-sensor-* token.
For a full custom bootstrap example, see Appendix A: Custom Bootstrap Script. For installation failure handling, see What if VM installation fails because prerequisites are missing?.
Identifier Mapping#
Use stable identifiers from the Hoster or Partner system.
| Field | Meaning | Model A example | Model B example |
|---|---|---|---|
external_user_id | Customer/account ID in the provisioning system | customer-987 | partner-a-customer-987 |
host_external_id | VM ID in the provisioning system | vm-12345 | partner-a-vm-12345 |
partner_id | Partner namespace for Model B provisioning | omitted | partner-a |
label | Human-readable Sensor/deployment label | customer-987-vm-12345 | customer-987-vm-12345 |
Use prefixes in Model B. They make scoping and audit trails clear.
Idempotency-Key#
Use Idempotency-Key on POST /api/v1/reseller/sensor-deployments.
In simple words, the idempotency key tells Imunify for AI agents: “if this exact VM creation request is retried, treat it as the same request, not a new VM.”
The key is created by the system that creates the VM:
- in Model A, the Hoster provisioning backend creates it
- in Model B, the Partner provisioning backend creates it
Good values are stable IDs from the VM provisioning workflow:
Idempotency-Key: vm-create-vm-12345
Idempotency-Key: vm-create-partner-a-vm-12345
Idempotency-Key: provision-order-7788-vm-12345
This makes retries safe and avoids duplicate deployment sessions when a worker restarts, a network call times out, or the caller loses the first response.
Important: the raw depbt_* token is returned only when the deployment session
is first created. If the first API response is lost and a retry returns the
same deployment by idempotency key, the response will not include the raw
deployment token again. If the VM still needs a token and the deployment is
still pending, rotate the deployment token with
POST /api/v1/reseller/sensor-deployments/{deployment_id}/rotate-token.
For practical recovery cases, see
Should I rotate a token or create a new deployment?
and
What if I get 409 Idempotency-Key payload mismatch?.
Status and Stats#
The deployment-create response contains deployment_id, for example dep_....
This is the stable ID of the deployment session. Use it later to check status,
rotate a lost deployment token, or cancel an unfinished deployment.
The response also contains:
status_url, which is the exact status path for that deploymentexpires_at, which is the UTC timestamp when thedepbt_*deployment token expires
Check deployment status with:
curl -sS \
-H "Authorization: Bearer sk-deployer-..." \
"https://panel.imunify.ai/api/v1/reseller/sensor-deployments/dep_..."
Here dep_... means the deployment_id returned by
POST /api/v1/reseller/sensor-deployments.
For a full explanation, see
How do I check the status of one VM deployment?.
Use reseller stats endpoints to monitor protected AI runtimes:
- current active count:
GET /api/v1/reseller/stats/protected-agents/current - month-level distinct count:
GET /api/v1/reseller/stats/protected-agents/monthly?month=YYYY-MM - daily rows for a calendar month:
GET /api/v1/reseller/protected-agents?month=YYYY-MM
Detailed examples and failure-handling scenarios are in Appendix B: Operational Q&A and Troubleshooting. Monthly usage examples are in How do I get protected runtime usage for a calendar month?.
Common deployment statuses:
| Status | Meaning |
|---|---|
pending | Deployment token was created but has not been claimed by a VM. |
claimed | VM exchanged depbt_* for sk-sensor-* and installer config. |
registered | Sensor installed and registered with Imunify for AI agents. |
active | Sensor is active and reporting. |
failed | Claim or installation failed. Check status_reason and last_claim_error. |
expired | depbt_* expired before claim. |
cancelled | Deployment was cancelled before completion. |
Response fields such as deployment_token, status_reason, cancelled_at, or
last_claim_error can be null. That is normal: they are populated only when
that value is relevant for the current deployment state.
See How do I check the status of one VM deployment?
for examples of those fields in context.
The default depbt_* lifetime is 1 hour; see
What if the deployment token expired?.
Operational Guidance#
Scope provisioning tokens tightly:
- use
partner_idfor Partner integrations - use
allowed_external_user_prefixes - use
allowed_host_external_id_prefixes - configure rate limits
- revoke unused provisioning tokens
Do not store long-lived credentials in:
- VM templates
- cloud-init user data
- customer-visible metadata
- support scripts copied to the VM
Only the one-time depbt_* token should be injected into the VM, and only at
VM creation time.
If the token is lost before the VM claims it, use
How do I rotate a lost deployment token?.
If the deployment should no longer be used, use
How do I cancel an unfinished deployment?.
Security Checklist#
sk-reseller-*is stored only in the Hoster or Platform Reseller backend.sk-deployer-*is stored only in the Hoster or Partner provisioning backend.- VM image contains only static bootstrap logic.
- VM receives only
depbt_*, and only for its own deployment. - Installer checksum is verified before execution.
- Provisioning tokens are scoped with prefixes and rate limits.
- Deployment creation uses
Idempotency-Key. external_user_idandhost_external_idare stable and unique.
Appendix A: Custom Bootstrap Script#
This example shows what a Hoster or Partner-maintained bootstrap script must do
if it does not call Imunify for AI agents’s ready-made /api/v1/install/bootstrap.sh
script.
Use this approach only when the integration needs custom behavior around the install flow. For most integrations, the simpler and safer approach is to keep the VM template small and call the Imunify for AI agents bootstrap script.
Requirements for a custom bootstrap:
- accept only
depbt_*deployment tokens - call
/api/v1/install/deployments/claim - never print or log the returned
sk-sensor-* - download both installer and checksum from the claim response
- verify SHA256 before executing the installer
- pass the returned values to the installer through
IMUNIFYAI_*environment variables - optionally report success/failure with
/api/v1/install/deployments/{id}/reportand the returneddeprt_*report token
Example:
#!/usr/bin/env bash
set -euo pipefail
: "${IMUNIFY_DEPLOYMENT_TOKEN:?missing IMUNIFY_DEPLOYMENT_TOKEN}"
IMUNIFY_BASE_URL="${IMUNIFY_BASE_URL:-https://panel.imunify.ai}"
case "$IMUNIFY_DEPLOYMENT_TOKEN" in
depbt_*) ;;
sk-admin-*|sk-reseller-*|sk-deployer-*|sk-user-*|sk-sensor-*)
echo "Refusing API token where depbt_* deployment token is required" >&2
exit 2
;;
*)
echo "Expected depbt_* deployment token" >&2
exit 2
;;
esac
command -v curl >/dev/null 2>&1 || {
echo "curl is required" >&2
exit 1
}
command -v jq >/dev/null 2>&1 || {
echo "jq is required" >&2
exit 1
}
command -v sha256sum >/dev/null 2>&1 || {
echo "sha256sum is required" >&2
exit 1
}
work_dir="$(mktemp -d /tmp/imunify-bootstrap.XXXXXX)"
cleanup() { rm -rf "$work_dir"; }
trap cleanup EXIT
claim_json="$work_dir/claim.json"
installer="$work_dir/sensor-installer.sh"
installer_sha="$work_dir/sensor-installer.sh.sha256"
hostname_value="$(hostname 2>/dev/null || printf unknown)"
arch_value="$(uname -m 2>/dev/null || printf unknown)"
curl -fsS -X POST "${IMUNIFY_BASE_URL%/}/api/v1/install/deployments/claim" \
-H "Content-Type: application/json" \
-d "$(jq -nc \
--arg token "$IMUNIFY_DEPLOYMENT_TOKEN" \
--arg hostname "$hostname_value" \
--arg arch "$arch_value" \
'{
deployment_token: $token,
hostname: $hostname,
arch: $arch,
os: "linux",
bootstrap_version: "custom-v1"
}')" \
-o "$claim_json"
deployment_id="$(jq -r '.deployment_id' "$claim_json")"
server_url="$(jq -r '.server_url' "$claim_json")"
sensor_token="$(jq -r '.sensor_token' "$claim_json")"
installer_url="$(jq -r '.installer_url' "$claim_json")"
installer_sha256_url="$(jq -r '.installer_sha256_url' "$claim_json")"
report_token="$(jq -r '.report_token // empty' "$claim_json")"
target_processes="$(jq -r '(.target_processes // []) | join(",")' "$claim_json")"
sensor_label="$(jq -r '.label // ""' "$claim_json")"
install_mitmproxy="$(jq -r 'if .install_mitmproxy then "1" else "0" end' "$claim_json")"
for value_name in deployment_id server_url sensor_token installer_url installer_sha256_url; do
value="${!value_name:-}"
if [ -z "$value" ] || [ "$value" = "null" ]; then
echo "Claim response missing ${value_name}" >&2
exit 1
fi
done
report_status() {
status="$1"
phase="$2"
reason="${3:-}"
if [ -z "$report_token" ]; then
return 0
fi
curl -fsS -X POST \
"${IMUNIFY_BASE_URL%/}/api/v1/install/deployments/${deployment_id}/report" \
-H "Content-Type: application/json" \
-d "$(jq -nc \
--arg token "$report_token" \
--arg status "$status" \
--arg phase "$phase" \
--arg reason "$reason" \
--arg hostname "$hostname_value" \
'{
report_token: $token,
status: $status,
phase: $phase,
reason: (if $reason == "" then null else $reason end),
hostname: $hostname,
bootstrap_version: "custom-v1"
}')" >/dev/null 2>&1 || true
}
report_status in_progress download
curl -fsSL "$installer_url" -o "$installer"
curl -fsSL "$installer_sha256_url" -o "$installer_sha"
expected_hash="$(cut -d' ' -f1 "$installer_sha")"
actual_hash="$(sha256sum "$installer" | cut -d' ' -f1)"
if [ -z "$expected_hash" ] || [ "$expected_hash" != "$actual_hash" ]; then
report_status failed verify "installer sha256 mismatch"
echo "FATAL: installer sha256 mismatch" >&2
exit 1
fi
export IMUNIFYAI_SERVER_URL="$server_url"
export IMUNIFYAI_SENSOR_TOKEN="$sensor_token"
export IMUNIFYAI_TARGET_PROCESSES="$target_processes"
export IMUNIFYAI_SENSOR_LABEL="$sensor_label"
export IMUNIFYAI_INSTALL_MITMPROXY="$install_mitmproxy"
export IMUNIFYAI_INSTALLER_URL="$installer_url"
export IMUNIFYAI_INSTALLER_SHA256_URL="$installer_sha256_url"
report_status in_progress install
if bash "$installer"; then
report_status succeeded install
else
rc="$?"
report_status failed install "installer exited with ${rc}"
exit "$rc"
fi
The custom script above is intentionally more verbose than the minimal VM bootstrap. It shows all moving parts so an integrator can adapt the flow while preserving the security properties of the Imunify for AI agents-provided bootstrap script.
Appendix B: Operational Q&A and Troubleshooting#
This appendix is organized by common operating tasks. Use it when the VM provisioning flow needs to check status, report usage, recover from a failed installation, retry a deployment, or troubleshoot Partner access.
How do I check the status of one VM deployment?#
Use the deployment_id returned by deployment creation. Deployment IDs start
with dep_. In examples, dep_... means “replace this with the real
deployment_id.”
Deployment creation returns both deployment_id and status_url:
{
"deployment_id": "dep_...",
"deployment_token": "depbt_...",
"expires_at": "2026-05-25T11:00:00+00:00",
"status_url": "/api/v1/reseller/sensor-deployments/dep_..."
}
Use that deployment_id in status, rotate, cancel, and troubleshooting calls.
Use expires_at to confirm whether the VM can still claim the depbt_* token.
Status request:
curl -sS \
-H "Authorization: Bearer sk-deployer-..." \
"https://panel.imunify.ai/api/v1/reseller/sensor-deployments/dep_..."
Example successful response:
{
"deployment_id": "dep_...",
"deployment_token": null,
"deployment_token_prefix": "depbt_...",
"reseller_id": "res_...",
"partner_id": "partner-a",
"user_id": "usr_...",
"external_user_id": "partner-a-customer-987",
"host_external_id": "partner-a-vm-12345",
"sensor_id": "sen_...",
"label": "customer-987-vm-12345",
"target_processes": ["ai-agent-runtime"],
"status": "registered",
"status_reason": null,
"created_at": "2026-05-25T10:00:00+00:00",
"expires_at": "2026-05-25T11:00:00+00:00",
"claimed_at": "2026-05-25T10:01:00+00:00",
"registered_at": "2026-05-25T10:02:00+00:00",
"cancelled_at": null,
"metadata": {},
"claim_attempt_count": 1,
"last_claim_error": null
}
Why is deployment_token null here?
This is expected. The raw depbt_* deployment token is shown only once, when
the deployment is created or when a pending deployment token is rotated. Status
responses intentionally do not return the raw token. They show only
deployment_token_prefix so operators can identify which token was issued
without exposing the secret again.
Why is status_reason null?
status_reason is populated only when there is something to explain, such as a
failure or cancellation reason. For normal states like pending, claimed,
registered, or active, status_reason: null means there is no error reason.
The same applies to cancelled_at, last_claim_error, and similar fields:
null means that condition has not happened.
If the provisioning system lost the raw depbt_* before the VM claimed it,
rotate the token while the deployment is still pending; see
How do I rotate a lost deployment token?.
What does expires_at mean?
For a pending deployment, expires_at is the time when the one-time depbt_*
deployment token stops being claimable. The default lifetime is 1 hour from
deployment creation or token rotation. Create the deployment close to VM boot,
not hours before the VM will be provisioned.
How do I get current protected runtime count?#
Use:
curl -sS \
-H "Authorization: Bearer sk-reseller-..." \
"https://panel.imunify.ai/api/v1/reseller/stats/protected-agents/current"
Example response:
{
"reseller_id": "res_...",
"usage_date": "2026-05-25",
"current_active_protected_agents": 1
}
How do I get protected runtime usage for a calendar month?#
There are two useful monthly views.
For the month-level distinct count, use:
curl -sS \
-H "Authorization: Bearer sk-reseller-..." \
"https://panel.imunify.ai/api/v1/reseller/stats/protected-agents/monthly?month=2026-05"
Example response:
{
"month": "2026-05",
"reseller_id": "res_...",
"distinct_protected_agents": 17,
"users": [
{
"user_id": "usr_...",
"external_user_id": "customer-987",
"distinct_protected_agents": 3,
"max_protected_agents": 10,
"entitlement_status": "ok"
}
],
"quality": {
"ok": 17,
"conflict": 0,
"missing_owner": 0
}
}
For daily rows within the calendar month, use:
curl -sS \
-H "Authorization: Bearer sk-reseller-..." \
"https://panel.imunify.ai/api/v1/reseller/protected-agents?month=2026-05"
This returns one row per observed protected runtime per UTC usage day, with pagination fields:
{
"protected_agents": [
{
"usage_date": "2026-05-25",
"user_id": "usr_...",
"external_user_id": "customer-987",
"sensor_id": "sen_...",
"protected_agent_id": "ai-agent-runtime",
"runtime_kind": "ai-agent",
"attribution_quality": "ok",
"last_runtime_state": "active",
"last_seen_at": "2026-05-25T10:05:00+00:00",
"host_native": true,
"plugin_version": "v0.2.19"
}
],
"count": 1,
"total": 1,
"limit": 500,
"offset": 0
}
Use the monthly stats endpoint for a summary count. Use the
protected-agents?month=YYYY-MM endpoint when you need daily detail or an audit
trail.
How do I get currently active protected runtimes?#
curl -sS \
-H "Authorization: Bearer sk-reseller-..." \
"https://panel.imunify.ai/api/v1/reseller/protected-agents?current=true"
Example response:
{
"protected_agents": [
{
"usage_date": "2026-05-25",
"user_id": "usr_...",
"external_user_id": "partner-a-customer-987",
"sensor_id": "sen_...",
"sensor_installation_id": "sensor-installation-id",
"protected_agent_id": "ai-agent-runtime",
"runtime_kind": "ai-agent",
"attribution_quality": "ok",
"attribution_source": "runtime-integration",
"last_runtime_state": "active",
"last_seen_at": "2026-05-25T10:05:00+00:00",
"last_runtime_scope_id": "host-native",
"container_name": null,
"host_native": true,
"plugin_version": "v0.2.19"
}
],
"count": 1,
"total": 1,
"limit": 500,
"offset": 0
}
What if VM installation fails because prerequisites are missing?#
Example: the Sensor installer fails because the VM kernel configuration does not have required prerequisites such as active eBPF LSM support.
Recommended flow:
Check deployment status:
curl -sS \ -H "Authorization: Bearer sk-deployer-..." \ "https://panel.imunify.ai/api/v1/reseller/sensor-deployments/dep_..."Inspect
status,status_reason, andlast_claim_error.Fix the VM template, image, kernel boot flags, package set, or provisioning step that caused the prerequisite failure.
If the deployment is still
pending, rotate the deployment token and retry the same deployment.If the deployment is
claimedbut the Sensor never registered, cancel the deployment and create a fresh deployment for the same VM after fixing the prerequisite issue.If the deployment is
registeredoractive, do not cancel it. The Sensor has already registered.
Should I rotate a token or create a new deployment?#
Use this rule:
| Situation | Action |
|---|---|
Deployment is pending and the raw depbt_* was lost | Rotate token. |
Deployment is pending and VM never started provisioning | Rotate token or cancel and recreate. |
Deployment is claimed and install failed before registration | Cancel and create a fresh deployment after fixing the VM issue. |
Deployment is expired | Create a fresh deployment. |
Deployment is cancelled or failed | Create a fresh deployment. |
Deployment is registered or active | Do not rotate or cancel; manage the installed Sensor. |
How do I rotate a lost deployment token?#
Rotation works only while the deployment is pending.
curl -sS -X POST \
-H "Authorization: Bearer sk-deployer-..." \
"https://panel.imunify.ai/api/v1/reseller/sensor-deployments/dep_.../rotate-token"
The response includes a new raw deployment_token and a new
bootstrap_command. The old depbt_* stops working.
How do I cancel an unfinished deployment?#
Cancel when the deployment should no longer be used. Cancellation is allowed for unfinished deployments and rejects registered/active deployments.
curl -sS -X POST \
-H "Authorization: Bearer sk-deployer-..." \
-H "Content-Type: application/json" \
"https://panel.imunify.ai/api/v1/reseller/sensor-deployments/dep_.../cancel" \
-d '{"reason":"VM provisioning failed before Sensor registration"}'
Example response:
{
"status": "cancelled",
"deployment_id": "dep_..."
}
What if the VM was recreated with the same VM ID?#
Use the same host_external_id if it represents the same VM identity in your
system. If an old deployment is still pending or claimed, cancel it before
creating the replacement deployment. If an old Sensor already registered for
the previous VM, treat that as an installed Sensor lifecycle action rather than
a deployment-token retry.
What if I get 409 Active deployment already exists for host_external_id?#
There is already an unfinished or active deployment for that VM ID.
Recommended actions:
Query deployments for that VM:
curl -sS \ -H "Authorization: Bearer sk-deployer-..." \ "https://panel.imunify.ai/api/v1/reseller/sensor-deployments?host_external_id=vm-12345"If the existing deployment is still valid and
pending, rotate its token.If it is stale and unfinished, cancel it and create a new deployment.
If it is
registeredoractive, the Sensor is already installed for that VM identity.
What if I get 409 Idempotency-Key payload mismatch?#
The same Idempotency-Key was reused with different VM/customer identifiers.
This usually means the provisioning system generated the key too broadly.
Fix the provisioning code so the idempotency key uniquely identifies one VM creation operation, for example:
Idempotency-Key: vm-create-<vm-id>
Do not reuse the same idempotency key for different VMs.
What if the Partner cannot access a deployment?#
Partner provisioning tokens are scoped. A Partner can access only deployments
inside its own partner_id and allowed ID prefixes. A deployment outside that
scope may return 404 instead of exposing that it exists.
Check:
- the Partner used the correct
sk-deployer-* partner_idmatches the provisioning token scopeexternal_user_idstarts with an allowed prefixhost_external_idstarts with an allowed prefix- the token has the needed scope, such as
sensor_deployments:read,sensor_deployments:create, orsensor_deployments:cancel
What if the deployment is stuck in claimed?#
claimed means the VM exchanged depbt_* for sk-sensor-*, but the Sensor did
not register yet.
Common causes:
- installer failed after claim
- VM lost network access during installation
- prerequisites were missing
- VM was destroyed before Sensor registration
- custom bootstrap did not run the installer or did not pass
IMUNIFYAI_*values correctly
Recommended action:
- Inspect the VM provisioning logs.
- Check deployment status for
status_reasonandlast_claim_error. - Fix the VM image/template/provisioning issue.
- Cancel the stuck deployment if it will not complete.
- Create a fresh deployment for the VM.
What if the deployment token expired?#
The one-time depbt_* deployment token expires at the response’s expires_at
timestamp. The default lifetime is 1 hour, controlled by
token_lifetime_seconds.
If the deployment is still pending but expires_at is in the past, create a
fresh deployment session. Expired deployment tokens cannot be claimed. If the
deployment is still pending and not expired but the raw token was lost, rotate
it instead.
The deprt_* report token is separate. It is returned after deployment claim and
expires after 30 minutes by default, controlled by
report_token_lifetime_seconds.
What if only install progress reporting is broken?#
If the VM installed successfully but the bootstrap report token was leaked or should no longer be accepted, revoke only the report token:
curl -sS -X POST \
-H "Authorization: Bearer sk-deployer-..." \
"https://panel.imunify.ai/api/v1/reseller/sensor-deployments/dep_.../revoke-report-token"
This affects only future install reports for that deployment. It does not stop an installed Sensor.