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:

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:

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#

TokenExampleOwnerUsed byPurposeLifetimePut in VM image?
Reseller backend tokensk-reseller-*Hoster or Platform Reseller tenantTenant backendManage tenant namespace, create users, create provisioning tokens, create deploymentsUntil revoked/rotatedNo
Provisioning tokensk-deployer-*Hoster automation or Partner automationProvisioning backendCreate/read/cancel deployment sessions within scoped limitsUntil revoked/rotatedNo
Deployment tokendepbt_*One VM deployment attemptVM bootstrap scriptOne-time token exchanged for Sensor token and installer configDefault 1 hourInject at provisioning time only
Sensor tokensk-sensor-*Installed SensorSensor serviceAuthenticates Sensor to Imunify for AI agentsUntil revoked/rotatedNo; bootstrap receives it during claim
Report tokendeprt_*VM bootstrapBootstrap report stepReports install progress/failure after claimDefault 30 minutesNo; 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:

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:

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:

The Partner:

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:

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:

  1. Validate that the provided token is a depbt_* deployment token, not a reseller, provisioning, user, admin, or Sensor API token.
  2. Claim depbt_* via /api/v1/install/deployments/claim.
  3. Receive server_url, sk-sensor-*, installer URL, checksum URL, label, report token, and target processes.
  4. Download the Sensor installer.
  5. Download the installer .sha256.
  6. Verify the installer SHA256 before execution.
  7. Run the verified installer with IMUNIFYAI_* environment variables.
  8. 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:

  1. Validates host prerequisites such as Linux support, required tools, BTF, and active eBPF LSM support.
  2. Installs the Imunify Sensor binary and configuration.
  3. Writes the Sensor token and server URL received from the deployment claim.
  4. Installs and registers the AI runtime integration plugin when that integration is enabled for the deployment profile.
  5. Installs systemd units for the Sensor and upgrade/rollback helpers.
  6. Starts or restarts the required services.
  7. 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.

FieldMeaningModel A exampleModel B example
external_user_idCustomer/account ID in the provisioning systemcustomer-987partner-a-customer-987
host_external_idVM ID in the provisioning systemvm-12345partner-a-vm-12345
partner_idPartner namespace for Model B provisioningomittedpartner-a
labelHuman-readable Sensor/deployment labelcustomer-987-vm-12345customer-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:

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:

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:

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:

StatusMeaning
pendingDeployment token was created but has not been claimed by a VM.
claimedVM exchanged depbt_* for sk-sensor-* and installer config.
registeredSensor installed and registered with Imunify for AI agents.
activeSensor is active and reporting.
failedClaim or installation failed. Check status_reason and last_claim_error.
expireddepbt_* expired before claim.
cancelledDeployment 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:

Do not store long-lived credentials in:

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#

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:

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:

  1. Check deployment status:

    curl -sS \
      -H "Authorization: Bearer sk-deployer-..." \
      "https://panel.imunify.ai/api/v1/reseller/sensor-deployments/dep_..."
    
  2. Inspect status, status_reason, and last_claim_error.

  3. Fix the VM template, image, kernel boot flags, package set, or provisioning step that caused the prerequisite failure.

  4. If the deployment is still pending, rotate the deployment token and retry the same deployment.

  5. If the deployment is claimed but the Sensor never registered, cancel the deployment and create a fresh deployment for the same VM after fixing the prerequisite issue.

  6. If the deployment is registered or active, do not cancel it. The Sensor has already registered.

Should I rotate a token or create a new deployment?#

Use this rule:

SituationAction
Deployment is pending and the raw depbt_* was lostRotate token.
Deployment is pending and VM never started provisioningRotate token or cancel and recreate.
Deployment is claimed and install failed before registrationCancel and create a fresh deployment after fixing the VM issue.
Deployment is expiredCreate a fresh deployment.
Deployment is cancelled or failedCreate a fresh deployment.
Deployment is registered or activeDo 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:

  1. 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"
    
  2. If the existing deployment is still valid and pending, rotate its token.

  3. If it is stale and unfinished, cancel it and create a new deployment.

  4. If it is registered or active, 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:

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:

Recommended action:

  1. Inspect the VM provisioning logs.
  2. Check deployment status for status_reason and last_claim_error.
  3. Fix the VM image/template/provisioning issue.
  4. Cancel the stuck deployment if it will not complete.
  5. 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.

Last updated May 25, 2026