Since there isn't official support for the API from Quest and the documentation provided is, well...lacking. I thought I would post what I use to interface with the API with examples of the endpoints I've been able to use successfully.

One of the biggest hurdles is incorrect paths in the documentation. For instance, /api/mi/managed_installs/add_to_machines is in the documentation, but the actual endpoint is /api/managed_install/managed_installs/add_to_machines. Some endpoints don't return the data they say they do, like /api/managed_install/machines/{MachineID}. it says it returns the MIs that the machine ID is a part of, but it just returns the machine information...which is useless (you could just use /api/inventory/machines).

Anyway, all of that aside, the below code (Powershell function) has examples that use the correct paths, and are tested and working on my 8.0 appliance. I use Powershell 5, and can't guarantee functionality for any previous Powershell versions. Also, be careful, as the API does not prompt for confirmation before processing, it just does it. The examples are in the comment-based help (for use with the Get-Help cmdlet). The function supports both single and multiple ORG appliances. For single org appliances, either omit the organization parameter or use -Organization Default. For multiple orgs, just use the name of the org with the Organization parameter. You can also log into the system org using -Organization System.

Please add comments below for any other paths that you have been able to use. Especially ones that differ from the documentation or aren't explained well in the documentation.

Note: Sorry the formatting is so crazy - it's cleaner in ISE.
#Requires -Version 5

#Initialize the WebSession hash table
if (!$Script:KaceREST) {

    $Script:KaceREST = @{}


#Force TLS 1.2 (required for K1000 8.0)
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

function Send-KaceAPIRequest {
    This function provides an easy(ish) method to interface with the K1000 REST API. 
    THe function will automatically log in to the appliance and establish a web session for the specified organiztion.
    Multiple organizations can be used at once, and the function will maintain a separate web session for each org.
    Data is returned from the function as the content from the full HtmlWebResponseObject that has been converted from JSON into a PSObject using ConvertFrom-Json.
    The -FullContent parameter can be used to return the original HtmlWebResponseObject in its entirety.
      The Kace REST API documentation is rife with incorrect paths. If the paths in the below examples differ from the documentations, then (at least at this point), the documentation is wrong.
      I use Powershell 5. I have done no testing with any previous version of Powershell, nor do I plan to.
      My K1000 is on version 8.0. This function has only been tested with this version.
      Use at your own risk.

    The Url of the K1000. Must begin with either http:// or https://

    HTTP verb to be used in the request. Must be one of the following: GET, POST, PUT, DELETE, OPTIONS, PATCH

    The path to the API endpoint. Must begin with either /api or /ams. Ex. -URI /api/inventory/machines

    Query to be added the request. Must begin with a ?. If there are spaces, then the parameter must be enclosed in double or single quotes. Ex. -Query '?filtering=name eq MACHINE1'

.PARAMETER Organization
    The organization to send the request. Defaults to "Default"

    Only available if the Method is PUT, POST, or PATCH. Accepts a hash table @{Property1=Value1;Property2=Value2}.

.PARAMETER FullContent
    Switch that tells the function to return the full HtmlWebResponseObject instead of the default PsObject. Use for extra troubleshooting information.

Get all machines (limited to 50 devices by default)
    Send-KaceAPIRequest -K1000Url https://K1000 -Method GET -URI /api/inventory/machines

Get all machines (no limit) Note: ALL is case sensitive, but 0 can also be used
    Send-KaceAPIRequest -K1000Url https://K1000 -Method GET -URI /api/inventory/machines -Query '?paging=limit ALL'
    Send-KaceAPIRequest -K1000Url https://K1000 -Method GET -URI /api/inventory/machines -Query '?paging=limit 0'

Get machines based on filters

    Machines with Win 7 enterprise x64 - full machine data -uses Organization called Test
        Send-KaceAPIRequest -K1000Url https://K1000 -Method GET -URI /api/inventory/machines -Query '?shaping=machine all&paging=limit all&filtering=os_name eq Microsoft Windows 7 Enterprise x64' -Organization Test

    Machines with softwre that contains "Chrome" installed - limited machine and software data
        Send-KaceAPIRequest -K1000Url https://K1000 -Method GET -URI /api/inventory/machines -Query '?shaping=machine limited,software limited&paging=limit all&filtering=software.display_name co Chrome'

    Machines in a label named Test - Note: if using label ID ( to filter for devices in a SMART label, know that the ID is based on the manual label that the smart label is associated with, not the ID of the smart label
        Send-KaceAPIRequest -K1000Url https://K1000 -Method GET -URI /api/inventory/machines -Query '?shaping=machine all&paging=limit ALL& eq Test'

Add machine to Managed Install

    Get machines in set of names
        $Machines = Send-KaceAPIRequest -K1000Url https://K1000 -Method GET -URI /api/inventory/machines -Query '?shaping=machine limited&paging=limit ALL&filtering=name in MACHINE1;MACHINE2'

    Add machines from previous query to managed install with ID 111 in Org Test
    Note: defining the machine IDs as an array is necessary in case only one machine is being added
        Send-KaceAPIRequest -K1000Url https://K1000 -Method PUT -URI /api/managed_install/managed_installs/111/add_to_machines -Organization Test -Body @{Machines=[array]$Machines.Machines.Id}

Remove machines from Managed Install
    Remove same machines from above add request
        Send-KaceAPIRequest -K1000Url https://K1000 -Method PUT -URI /api/managed_install/managed_installs/111/remove_from_machines -Organization Test -Body @{Machines=[array]$Machines.Machines.Id}

Run a script with ID 15 on machines in a label named Test and check the status after 5 seconds

    Get machines and format a string for the below post request (IDs must be separated by commas)
    $Machines = Send-KaceAPIRequest -K1000Url https://K1000 -Method GET -URI /api/inventory/machines -Query '?paging=limit 0&shaping=machine limited& eq Test'
    [string]$MachineIDs = $null

    foreach ($MachineID in $Machines.Machines.Id) {

        $MachineIDs += ',' + $MachineID


    $MachineIDs = $MachineIDs.Substring(1)
    Note: Returns 0 if failed
    $ScriptRun = Send-KaceAPIRequest -K1000Url https://K1000 -Method POST -URI /api/script/15/actions/run?machineIDs=$MachineIDs

    if ($ScriptRun[0] -ne 0) {

        Start-Sleep -Seconds 5

        #Check Status of request
        Send-KaceAPIRequest -K1000Url $K1Url -Method GET -URI /api/script/runStatus/$ScriptRun


    else {





    Param (

        [Parameter(Position=0,Mandatory=$true,HelpMessage='Must begin with ''http://'' or ''https://''')]
        [ValidateScript({$_ -like "http://*" -or $_ -like "https://*"})]
        [Parameter(Position=1,Mandatory=$true,HelpMessage='Insert a valid HTTP verb: GET, OPTIONS, POST, PUT, DELETE, or PATCH')]

        [Parameter(Position=2,Mandatory=$true,HelpMessage='Must begin with ''/api'' or ''/ams''')]
        [ValidateScript({$_ -like "/api*" -or $_ -like "/ams*"})]

        [ValidateScript({$_ -like "?*"})]


        [ValidateScript({$Script:KaceREST.Keys -contains $Organization})]



    DynamicParam {

        #Define Body paramter if method is PUT, POST, or PATCH
        if ($Method -in ('POST','PUT','PATCH')) {

            #Create parameter attributes
            $BodyParamAttributes = New-Object -Type System.Management.Automation.ParameterAttribute -Property @{

                HelpMessage='Accepts a hastable with the required properties that will be converted to JSON'


            #Add parameter attributes to collection
            $BodyParamAttributeCollection = New-Object -Type System.Collections.ObjectModel.Collection[System.Attribute]

            #Create parameter
            $BodyParam = New-Object -Type System.Management.Automation.RuntimeDefinedParameter("Body", [Hashtable],$BodyParamAttributeCollection)

            #Define and return parameter
            $BodyParamDictionary = New-Object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
            Return $BodyParamDictionary



    Begin {

        if ($ResetCookies) {



        if (!$Script:KaceREST."$Organization") {

            #Get credentials for login and create login JSON request
            #HTTPS is recommended since credentials are sent in plain text
            #If the password needs to be stored to be able to run API requests for automation, then please read the following articles for doing this as securely as possible.
            # (requires Powershell 5)
            try {
                $RestCredentials = Get-Credential -Message "Type in the K1000 REST Username/Password for Org $Organization"

                $RESTUserName = $RestCredentials.UserName

                if (!$RestCredentials -or $RestCredentials.Password.Length -eq 0) {
                    Throw "Operation cancelled by user or invalid credential object"

                $RESTPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR(($RestCredentials.Password)))

                $LoginBody = ConvertTo-Json -InputObject ([ordered]@{'password'=$RESTPassword;'userName'=$RESTUserName;'organizationName'=$Organization})


            catch {

                if ($_.Exception -eq 'Operation cancelled by user or invalid credential object') {

                    Write-Error -Message "Unable to create login request for $K1000Url`: Invalid credential object or operation cancelled by user."


                Write-Error -Message "$_`nUnable to create login request for $K1000Url.`nPlease check that the credentials are correct."



            #Log in to the API

            try {

                #Create key in the hash table for specified ORG to store Web session
                $script:KaceREST.Add($Organization,(New-Object Microsoft.PowerShell.Commands.WebRequestSession))
                #Post login request
                $LoginRequest = Invoke-WebRequest -Uri ("$K1000Url" + "/ams/shared/api/security/login") -Method Post -ContentType 'application/json' -Headers @{'Accept'='application/json';'x-dell-api-version'=1} -Body $LoginBody -WebSession $script:KaceREST.$Organization -ErrorAction Stop

                #Add CSRF token to web session


            catch {
                #If login failed, clear the web session from the table

                Write-Error -Message "$_`nUnable to log in to REST API at $K1000Url.`nPlease check that the Url and credentials are correct, and that mobile access is enabled for the specified user."




    Process {

        try {
            #Build the request URL
            $RequestUri = "$K1000Url" + "$Uri" + "$Query"

            #Convert the body hash table to JSON
            if ($PSBoundParameters.Body) {

                $Body = ConvertTo-Json -InputObject $PSBoundParameters.Body

            #Send the request. -ContentType and Body parameters are required for PUT/POST requests. They are here for all requests because it simplifies coding and are ignored for the GET method anyway.
            $Response = Invoke-WebRequest -Uri $RequestUri -Method $Method -WebSession $Script:KaceREST.$Organization -ContentType 'application/json' -Body $Body -ErrorAction Stop


        catch {

            Write-Error -Message "$_`n$Method request to `'$RequestUri`' for the $Organization org failed.`nPlease check that the API path is correct and mobile access is enabled for the specified user.`nUse -ResetCookies if you think that the token has expired."



    End {

        #Return the response in friendly format, which is the content from the original HtmlWebResponseObject, converted from JSON to a PSObject (ConvertFrom-JSON). 
        #Use -FullContent to return the original HtmlWebResponseObject
        if ($Response.GetType().Name -eq 'HtmlWebResponseObject') {

            if ($FullContent) {



            else {

                ConvertFrom-JSON -InputObject $Response.Content



        else {

            Write-Error -Message "Unable to return HTML response"




#Define K1 URL amd Organization name
$K1Url = "https://K1000"
$OrgName = "Default"

#Get all machines (limited to 50 devices by default)
$Machines = Send-KaceAPIRequest -K1000Url $K1Url -Method GET -URI /api/inventory/machines -Organization $OrgName