Agent Upgrade API

The Agent Upgrade API enables automated upgrading of software agents by listing available versions and triggering upgrades for specified agent installations in a single call

This document provides detailed information on using the Agent Upgrade API, designed to simplify and consolidate the agent upgrade process into two main endpoints for headless CI/CD usage.

Overview

The existing UI upgrade flow requires five sequential API calls under the internal /controller/restui/ path. With this API, you can consolidate these calls into two endpoints under /controller/rest/, designed for automated and headless CI/CD workflows:

Endpoint Method Purpose
/controller/rest/agent-management/v2/upgrades/versions GET List available agent upgrade versions
/controller/rest/agent-management/v2/upgrades POST Trigger agent upgrade (validate and create in one call)
To track upgrade progress after triggering, use the existing task status API, /controller/restui/agent-management/inventory/tasks/ids with the following payload:
JSON
{
    "requestFilter": [
        12, 13
    ],
    "resultColumns": [
    ],
    "offset": 0,
    "limit": 10,
    "searchFilters": [],
    "columnSorts": [],
    "timeRangeStart": 0,
    "timeRangeEnd": 0
}
Sample Response:
JSON
{
    "id": 1,
    "version": 0,
    "name": "Deploy updated configuration",
    "type": "CONFIGURE",
    "triggerTs": 1762941714549,
    "createdBy": 5,
    "createTs": 1762941714549,
    "modifiedBy": 5,
    "modifyTs": 1762941714549,
    "accountId": 2,
    "deployments": [
        {
            "id": 1,
            "version": 1,
            "taskId": 1,
            "agentType": "SMART_AGENT",
            "smartAgentId": "01K2KDF2TZA83RP71HB6WB40XN",
            "agentInstanceId": "01K2KDF2TZA83RP71HB6WB40XN",
            "configYaml": "{}\n",
            "configHash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
            "configFilesJson": "[{\"fileName\":\"config.json\",\"fileFormat\":\"json\",\"fileContent\":\"{\\\"key\\\": \\\"value\\\"}\",\"targetPath\":\"/opt/appdynamics/config/\"}]",
            "status": "TRIGGERED",
            "logs": "",
            "agentId": null,
            "endTs": 1762941714550,
            "deploymentV1": 0,
            "config": {}
        }
    ],
    "profileId": 0
}

Prerequisites

Before calling the upgrade APIs, obtain the agent installation V2 IDs from the existing public Agent Inventory API:
JSON
curl -X POST "https://<controller-host>/controller/rest/v2/agent-management/inventory/query"
 -H "Content-Type: application/json"
 -u "user@account:password"
 -d '{ "agentType": "SMART_AGENT", "limit": 25, "offset": 0 }'
Use the correct ID field from the inventory response:
  • agentId: legacy agent entity ID

  • agentInstallationV2: agent installation V2 ID (use this as the value for agentInstallationIds in the upgrade API).

    For example, if the inventory response contains "agentId": 100, "agentInstallationV2": 2, pass "agentInstallationIds": [2] to the upgrade API.

Get Available Upgrade Versions

Endpoint: GET /controller/rest/agent-management/v2/upgrades/versions?agentType={agentType}

Description: Returns a list of available agent versions that can be used as the targetVersion in an upgrade request. Versions are ordered from newest to oldest.

Authorization: Requires Agent Management Viewer role (read-only access).

Query Parameters:

Parameter Type Description
agentType String Agent type (see Supported Agent Types below)

Supported Agent Types:

agentType Value Description Aliases (also accepted)
SMART_AGENT Smart Agent smart_agent, smartagent
APP_AGENT Java App Server Agent app, java, java_agent
MACHINE_AGENT Machine Agent machine, machine_agent
DB_AGENT Database Agent db, db_agent
DOT_NET_APP_AGENT .NET Agent dotnet, .net, dot_net
PHP_APP_AGENT PHP Agent php
NODEJS_APP_AGENT Node.js Agent node, nodejs
PYTHON_APP_AGENT Python Agent python
NATIVE_WEB_SERVER Native Web Server Agent webserver

Sample Request:

CODE
curl -X GET "https://<controller-host>/controller/rest/agent-management/v2/upgrades/versions?agentType=SMART_AGENT"
 -H "Accept: application/json"
 -u "user@account:password"

Sample Response (200 OK):

JSON
{ "agentType": "SMART_AGENT", "versions": [ "26.2.0.779", "25.12.0.661", "25.10.0.500", "25.8.0.432" ] }

Error Responses:

Status Code Condition Example
400 Bad Request Missing or invalid agentType {"code":"validation-1","message":"Invalid agentType 'UNKNOWN'..."}
403 Forbidden Caller lacks viewer role {"code":"auth-1","message":"Agent management viewer role required"}

Upgrade Agents

Endpoint:

CODE
POST /controller/rest/agent-management/v2/upgrades

Description: Triggers an agent upgrade in a single atomic call. This endpoint performs:

  • Validation: checks all agents for eligibility (active smart agent, no pending deployments).
  • Agent resolution: looks up smart agent IDs and metadata from installation IDs.
  • Config generation: builds the deployment YAML config from the provided parameters.
  • Task creation & trigger: creates the upgrade task and triggers it to the Fleet Management service.

Authorization: Requires Agent Management Admin role.

Request Body:

Field Type Required Default Description
agentInstallationIds List<Long> Yes Agent installation V2 IDs to upgrade. Use agentInstallationV2 from inventory API response.
agentType String Yes Agent type (see Supported Agent Types above).
targetVersion String Yes Version to upgrade to (from versions API).
downloadSource String No "appd" Download source: appd (AppD portal), local (local directory), http (HTTP URL), npm (npm registry, Node.js only).
downloadUri String Conditional null Required when downloadSource is local or http. Local file path or HTTP URL to the agent binary.
enableSsl Boolean No true Whether to enable SSL for agent-to-controller communication.
reuseConfig Boolean No true Whether to reuse existing agent config.
customConfig Map<String, String> No null Additional key-value properties for the deployment YAML (e.g., sim_enabled, flags). Reserved keys are silently ignored.
skipIneligible Boolean No true If true, skip ineligible agents and proceed. If false, fail if any agent is ineligible.
Note: The reserved keys in customConfig: The following keys are auto-derived from dedicated request fields and will be silently ignored if passed in customConfig:
  • install_agent_from

  • download_protocol, agent_version

  • download_uri, enable_ssl

  • reuse_config

Use the corresponding top-level request fields instead.

Configuration Derivation Logic: The API automatically derives internal configuration fields based on the downloadSource value. You do not need to set install_agent_from because it is automatically determined by the server.

downloadSource Derived install_agent_from downloadUri Required? Notes
appd (default) appd-portal No Downloads from AppDynamics portal
local local Yes (local path) This is an example: /opt/agents/binaries
http http Yes (HTTP URL) This is an example: https://artifacts.example.com/agent.zip
npm No Only valid for NODEJS_APP_AGENT

Sample Requests:

Minimal (Default Upgrade):

PYTHON
# agentInstallationIds should contain agentInstallationV2 values from the inventory API
curl -X POST "https://<controller-host>/controller/rest/agent-management/v2/upgrades" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -u "user@account:password" \
  -d '{
    "agentInstallationIds": [1, 2, 3],
    "agentType": "SMART_AGENT",
    "targetVersion": "26.2.0.779"
  }'

Full (Custom Upgrade):

JSON
curl -X POST "https://<controller-host>/controller/rest/agent-management/v2/upgrades" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -u "user@account:password" \
  -d '{
    "agentInstallationIds": [1, 2, 3],
    "agentType": "SMART_AGENT",
    "targetVersion": "26.2.0.779",
    "downloadSource": "http",
    "downloadUri": "https://artifacts.example.com/smart-agent-26.2.0.779.zip",
    "enableSsl": true,
    "reuseConfig": true,
    "customConfig": {
      "flags": "-Dsome.property=value",
      "sim_enabled": "true"
    },
    "skipIneligible": true
  }'

Sample Request for Node.js Agent using npm

JSON
curl -X POST "https://<controller-host>/controller/rest/agent-management/v2/upgrades" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -u "user@account:password" \
  -d '{
    "agentInstallationIds": [5, 6],
    "agentType": "NODEJS_APP_AGENT",
    "targetVersion": "26.1.0.100",
    "downloadSource": "npm"
  }'
Sample Request for Local Download Source
JSON
curl -X POST "https://<controller-host>/controller/rest/agent-management/v2/upgrades" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -u "user@account:password" \
  -d '{
    "agentInstallationIds": [1],
    "agentType": "SMART_AGENT",
    "targetVersion": "26.2.0.779",
    "downloadSource": "local",
    "downloadUri": "/opt/agents/binaries"
  }'
Sample Success Response for All Active Agents (201):
JSON
{
  "taskId": 456,
  "status": "TRIGGERED",
  "deploymentCount": 3,
  "deployments": [
    {
      "deploymentId": 4561,
      "agentId": 1,
      "status": "TRIGGERED",
      "agentType": "SMART_AGENT"
    },
    {
      "deploymentId": 4562,
      "agentId": 2,
      "status": "TRIGGERED",
      "agentType": "SMART_AGENT"
    },
    {
      "deploymentId": 4563,
      "agentId": 3,
      "status": "TRIGGERED",
      "agentType": "SMART_AGENT"
    }
  ],
  "skippedAgents": {}
}
Sample Partial Success Response (201 Created with inactive Agents):
JSON
{
  "taskId": 457,
  "status": "TRIGGERED",
  "deploymentCount": 1,
  "deployments": [
    {
      "deploymentId": 4571,
      "agentId": 1,
      "status": "TRIGGERED",
      "agentType": "SMART_AGENT"
    }
  ],
  "skippedAgents": {
    "INACTIVE_SMART_AGENT": [2],
    "EXISTING_DEPLOYMENTS": [3]
  }
}
Sample Response for No Active Agents (200 OK):
JSON
{
  "taskId": 0,
  "status": "NO_ELIGIBLE_AGENTS",
  "deploymentCount": 0,
  "deployments": [],
  "skippedAgents": {
    "INACTIVE_SMART_AGENT": [1, 2, 3]
  }
}

Error Responses:

Status Code Condition Description
400 Bad Request Missing/invalid required fields Missing agentInstallationIds, agentType, or targetVersion; invalid agentType or downloadSource value; missing downloadUri when required; npm used for non-Node.js agent
403 Forbidden Insufficient permissions Caller does not have Agent Management Admin role
409 Conflict Ineligible agents with skipIneligible=false Some or all agents failed validation; response body contains details
500 Internal Server Error Server-side failure Task creation failed, Fleet Management service unavailable, etc.

Error Response Format:

JSON
{
  "code": "validation-1",
  "message": "agentInstallationIds is required and cannot be empty",
  "refCode": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Security Considerations

All endpoints require HTTP Basic Authentication or API Token authentication. The format is:
  • Basic Auth: username@accountname:password

  • API Token: username@accountname:api-token

Session cookies (as used by the UI) are not required. These APIs are designed for headless/automated access.

Authorization (RBAC)

The following roles are required for API access:

Endpoint Required Role
GET /upgrades/versions Agent Management Viewer
POST /upgrades Agent Management Admin

Additionally, the upgrade endpoint enforces per-agent RBAC: the caller can only upgrade agents they have permission to manage. Agents outside the caller's permission scope are silently excluded.

Account Isolation

The buildAgentInfoForUpgrade method verifies that all requested agent installation IDs belong to the account associated with the request. Cross-account upgrade attempts are rejected to ensure account isolation.

Input Sanitization

  • Controller host name and port values in the deployment configuration are sanitized to ensure proper YAML string output.
  • The customConfig map values are added as strings to prevent injection into the Ansible playbook YAML.
  • Reserved configuration keys (install_agent_from, download_protocol, agent_version, download_uri, enable_ssl, reuse_config) are silently stripped from customConfig to prevent callers from overriding auto-derived internal values.
  • Sensitive fields such as account access key and account name are populated server-side and never accepted from the client.
  • Unknown fields in the request JSON are silently ignored (@JsonIgnoreProperties(ignoreUnknown = true)).

Audit Logging

Every upgrade request is logged with the following details for traceability and auditing:

  • Unique reference code (refCode)
  • User identity and account ID
  • Number of agents targeted and number skipped
  • Task ID created

CI/CD Pipeline Example

JSON
#!/bin/bash
set -euo pipefail

CONTROLLER="https://controller.example.com:8090"
AUTH="apiuser@customer1:api-token-here"

# Step 1: Discover agents to upgrade
echo "=== Discovering agents ==="
AGENTS=$(curl -s -X POST "$CONTROLLER/controller/rest/v2/agent-management/inventory/query" \
  -H "Content-Type: application/json" \
  -u "$AUTH" \
  -d '{
    "agentType": "SMART_AGENT",
    "limit": 100,
    "offset": 0,
    "filters": [
      { "field": "VERSION_STATUS", "operator": "IN", "value": ["UpdateAvailable"] }
    ]
  }')

# IMPORTANT: Use agentInstallationV2 (not agentId) for the upgrade API
AGENT_INSTALLATION_IDS=$(echo "$AGENTS" | jq '[.data[].agentInstallationV2]')
echo "Found agent installation IDs: $AGENT_INSTALLATION_IDS"

# Step 2: Check available versions
echo "=== Checking available versions ==="
VERSIONS=$(curl -s -X GET "$CONTROLLER/controller/rest/agent-management/v2/upgrades/versions?agentType=SMART_AGENT" \
  -H "Accept: application/json" \
  -u "$AUTH")

LATEST=$(echo "$VERSIONS" | jq -r '.versions[0]')
echo "Latest version: $LATEST"

# Step 3: Trigger upgrade
echo "=== Triggering upgrade to $LATEST ==="
RESULT=$(curl -s -w "\n%{http_code}" -X POST "$CONTROLLER/controller/rest/agent-management/v2/upgrades" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -u "$AUTH" \
  -d "{
    \"agentInstallationIds\": $AGENT_INSTALLATION_IDS,
    \"agentType\": \"SMART_AGENT\",
    \"targetVersion\": \"$LATEST\",
    \"skipIneligible\": true
  }")

HTTP_CODE=$(echo "$RESULT" | tail -1)
BODY=$(echo "$RESULT" | head -1)

if [ "$HTTP_CODE" = "201" ]; then
  TASK_ID=$(echo "$BODY" | jq '.taskId')
  DEPLOY_COUNT=$(echo "$BODY" | jq '.deploymentCount')
  echo "SUCCESS: Task $TASK_ID created with $DEPLOY_COUNT deployments"
  echo "Track progress using task ID $TASK_ID via the existing task status API."
else
  echo "FAILED: HTTP $HTTP_CODE - $BODY"
  exit 1
fi