-
Notifications
You must be signed in to change notification settings - Fork 4
Jamf Pro Commands
Jamf Pro commands live under the pro namespace. For shared CLI patterns (apply, scaffold, pagination, patch, dry-run, destructive ops), see CLI Patterns.
jamf-cli pro <resource> <action> [flags]
Commands are generated against the Jamf Pro 11.32.0 API spec, which adds
pro sso-settings oidc-broker-config get/update(the tenant's OIDC broker configuration,/v3/sso/oidc-broker-config) andpro jamf-pro-notifications delete --all.
proresource names come from the API's own tags and URL paths: 38 resources renamed, 24 merged, 13 split. 100 former names resolve to their replacement until 2027-03-09, printing a warning that neither--quietnor--no-hintssilences. Three refuse, their endpoints no longer being ingested. Another 48 invocations moved their operation, so the alias resolves and the subcommand does not; each refuses and names the replacement. Full table: Pro Command Renames; this page uses the new names.
On a platform gateway profile (
auth-method: platform), 59procommand entries are refused before a request is sent, with exit code 8 andRefused by policy. Among them:pro computers lock/restart/shutdownand sixteenpro mobile-devicesactions. See Gateway Profiles: Refused Commands and Platform API GA Migration.
Platform API commands are reached through this same
pronamespace: blueprints, compliance benchmarks, platform devices, DDM reports. See Platform API Commands. Jamf AI Governance, Jamf Account and platform audit live under a top-levelplatformnamespace instead. See Jamf Platform Commands.
These are permanent and print no warning. The retired resource names in Pro Command Renames also resolve, but print a deprecation warning and stop working after 2027-03-09.
| Command | Alias |
|---|---|
computer-inventory |
computers, comp
|
mobile-devices |
md |
scripts |
scr |
buildings |
bld |
categories |
cat |
departments |
dept |
group-tools |
gt |
api-roles |
ar |
api-integrations |
ai |
jamf-protect |
jp |
computer-groups-smart-groups |
smart-computer-groups |
jamf-cloud-distribution-service |
jcds |
jamf-cloud-distribution-service-files |
jcds-files |
device |
dev |
config |
cfg |
Platform API resources reached through pro carry their own aliases (bp, cb, pdev, pdg, ddm); see Platform API Commands.
jamf-cli pro computers list # same as: jamf-cli pro comp list
jamf-cli pro mobile-devices list -o table # same as: jamf-cli pro md list -o table
jamf-cli pro scripts list # same as: jamf-cli pro scr listoverview fetches ~39 API endpoints in parallel and prints a consolidated summary of the instance. Under platform gateway auth it adds Platform API data.
- Health & Alerts: active alerts, server health, SLASA/CSA scopes
- Instance: server URL, Jamf Pro version, gateway URL (platform auth)
- Fleet: managed/unmanaged computer and mobile device counts, check-in frequency
- Enrollment & Certificates: ADE instances with per-instance token expiry, VPP locations with per-location token expiry, APNs certificate status, MDM auto-renewal, DEP sync status, prestages
- Configuration: policies, config profiles, packages, scripts, patch titles, App Installers, eBooks, webhooks
- Organization: sites, buildings, departments, categories, computer smart/static groups, mobile smart/static groups, static user groups
- Features: SSO (SAML + Admin), volume purchasing, DEP, cloud deployments, patch management, SMTP
jamf-cli pro overview # table format
jamf-cli pro overview -o json # JSON for scriptingpro open launches the Jamf Pro web interface in a browser. With no argument it opens the dashboard; with a section name it opens that page.
jamf-cli pro open # dashboard
jamf-cli pro open policies
jamf-cli pro open smart-computer-groups
jamf-cli pro open settings/api-roles-and-clients
jamf-cli pro open --list # every section name
jamf-cli pro open computers --print # print the URL instead of launching
jamf-cli pro open computers --field url # bare URL, for scripting139 sections are named. pro open --list prints them with the heading the interface uses for each (name, page, path), through the shared formatter, so -o json, -o csv and --select all work on it. Tab completion offers each name with its heading. Read the list from the binary rather than from here; it moves with the interface.
91 of the 139 are settings/-prefixed. Those names drop the interface's own category segment and the CLI restores it, so you do not have to know which settings category a page sits in:
pro open settings/api-roles-and-clients -> /view/settings/system-settings/api-roles-and-clients
Record-detail and wizard pages are deliberately unnamed: they need an id or mid-flow state. An argument that is not a section name but looks like a path is opened verbatim, which keeps those pages reachable.
jamf-cli pro open computers.html?id=42
jamf-cli pro open view/settings/system-settings/ssoExit 2 in each case, with nothing launched:
| Invocation | Message |
|---|---|
pro open ssso |
unknown section "ssso", hinting jamf-cli pro open --list and the path form. Suggestions match on substring before edit distance. |
pro open --list computers |
--list lists every section name and takes no section: drop --list to open "computers". The argument is refused, not discarded. |
pro open https://example.com/foo |
"https://example.com/foo" is a URL, not a section: this command opens a page of this Jamf Pro instance |
pro open ../../etc |
Unknown section. .. segments, including the %2e%2e spelling, and any resolved URL whose host is not the instance's, are refused; so is anything that is not absolute http or https. |
Under any of --print, --no-input, --dry-run, --out-file, --field, --select, or a non-terminal stdout. jamf-cli pro open computers | pbcopy and a CI job both get the URL rather than a browser launch attempt. Consume it with --field url for a bare string or -o json for a row. In the launch case stdout stays empty and the note goes to stderr, so nothing downstream reads a launch as data.
$BROWSER wins where it is set, then open / xdg-open / rundll32.
pro open is the only one of the four product open commands that can send a request.
| Profile | Requests |
|---|---|
instance (token or oauth2) |
none; the configured URL is used |
| platform gateway | exactly one: GET /v1/jamf-pro-server-url, scope jss-url:read ("Read JSS URL" on a Jamf Pro API role) |
A platform integration names a tenant, never a Jamf Pro host, which is why the gateway case has to ask. pro open --list needs no credentials at all: the section table is compiled into the binary.
protect open, school open and security open are the same command for the other three products. None of them sends a request, all three carry jamf:no-auth, and security open is the constant https://radar.wandera.com/ because Security Cloud has no per-tenant URL.
These commands send
/v4/computers-inventory, andgetreads the v4 detail endpoint. On a404the generated subcommands retry/v3, then/v2, then/v1, warning on stderr, so an instance on an older API keeps working.pro comp eraseandpro comp remove-mdmare hand-written, send/v4/computers-inventory/{id}/…with no fallback, and answer404on an instance that does not serve v4.Through a gateway profile,
-vshows the GA path shape (/pro/v4/…, no/apisegment) and the default section set:GET https://eu.api.jamfcloud.com/pro/v4/computers-inventory?section=GENERAL§ion=HARDWARE§ion=OPERATING_SYSTEM&page=0&page-size=2000
list and get hit the detail endpoint and return the GENERAL section by default. --section adds more (HARDWARE, OPERATING_SYSTEM, SECURITY, USER_AND_LOCATION, DISK_ENCRYPTION) and is repeatable. Run jamf-cli pro computers list --help for the full section list.
jamf-cli pro computers list --all -o table # List all computers
jamf-cli pro computers list --sort general.name:asc # Sorted by name
jamf-cli pro computers list --section HARDWARE --section OPERATING_SYSTEM # Add hardware + OS columns
jamf-cli pro computers get 42 -o json # Get a single computer by ID
jamf-cli pro computers get --name "Neil's MacBook" # Get by name
jamf-cli pro computers get --serial C02X1234 # Get by serial number
jamf-cli pro computers get 42 --section HARDWARE --section SECURITY # Include hardware + security detail
jamf-cli pro computers list --field general.name # Just the namesNote: the resource is
computer-inventory;computersandcompare permanent aliases.computers-inventoryresolves too, warns, and retires on 2027-03-09. Filter and sort fields live inside sections (general.name,hardware.serialNumber).
pro computer-inventory-collection-settings-custom-path createacceptsscope: APPonly (case-sensitive). It sendsPOST /v2/computer-inventory-collection-settings/custom-path; the withdrawn v1 accepted[APP, FONT, PLUGIN].--helplists no allowed values for it, and aFONTorPLUGINpath answers400.
list uses the detail endpoint and takes --section; get returns the full device record and takes none. list fetches HARDWARE for you, so its default table carries serial number and model. Common sections: GENERAL, HARDWARE, USER_AND_LOCATION, SECURITY, APPLICATIONS, NETWORK, CERTIFICATES. Run jamf-cli pro mobile-devices list --help for the full list.
jamf-cli pro mobile-devices list --all -o table # List all mobile devices (includes hardware by default)
jamf-cli pro mobile-devices list --section USER_AND_LOCATION # Include assigned user
jamf-cli pro mobile-devices get 7 # Get a single device by ID
jamf-cli pro mobile-devices get --serial F4K3SER1AL # Get by serial number
jamf-cli pro mobile-devices get --udid XXXXXXXX-XXXX-... # Get by UDID
jamf-cli pro mobile-devices list --section SECURITY --section NETWORK # Multiple detail sections (list only)jamf-cli pro scripts list -o table # List scripts
jamf-cli pro scripts get 15 -o json # Get a script by ID
jamf-cli pro scripts apply --scaffold > s.json # Generate template, edit, then apply
cat s.json | jamf-cli pro scripts apply --yes
jamf-cli pro scripts delete --name "Deploy Agent" --yes # Delete by namejamf-cli pro buildings list -o table # List buildings
jamf-cli pro buildings get --name "HQ" -o yaml # Get a building by name
jamf-cli pro categories list --field name # Category names only
# Apply (upsert): create or replace by name
echo '{"name":"HQ","city":"Cupertino"}' | jamf-cli pro buildings apply --yes
echo '{"name":"Security","priority":1}' | jamf-cli pro categories apply --yes
# Scaffold → edit → apply
jamf-cli pro departments apply --scaffold > d.json
cat d.json | jamf-cli pro departments apply --yes
# Delete by name
jamf-cli pro buildings delete --name "Old Office" --yes
jamf-cli pro categories delete --name "Unused Category" --yesClassic API commands (classic-*) return raw XML by default, the native wire format. Use -o to convert:
| Format | Description |
|---|---|
(none) / -o xml
|
Pretty-printed XML (default for Classic API) |
-o raw |
Exact bytes from the API, no processing |
-o json |
XML converted to JSON |
-o table |
Tabular view of selected fields |
# Default: pretty-printed XML
jamf-cli pro classic-policies get 100
# Force JSON output
jamf-cli pro classic-policies get 100 -o json
# Raw bytes, for piping to another tool
jamf-cli pro classic-policies get 100 -o raw > policy.xml
# Table view of the list
jamf-cli pro classic-policies list -o tableNote: Modern API commands (
computers,scripts,buildings) return JSON by default. The XML formats apply to Classic API commands only.
Classic create and update read their XML body from --from-file, from --set, or, with neither flag, from stdin. apply takes --from-file or --set the same way.
# From a file
jamf-cli pro classic-network-segments create --from-file segment.xml
# From stdin
cat segment.xml | jamf-cli pro classic-network-segments create
# Print a complete XML template, edit it, send it
jamf-cli pro classic-network-segments create --scaffold > segment.xml
# Build the whole body from flags: no file, no XML to write
jamf-cli pro classic-network-segments create \
--set name="Amsterdam Office" \
--set starting_address=10.1.1.1 \
--set ending_address=10.1.1.254
# Rename an existing segment and change nothing else
jamf-cli pro classic-network-segments update --name "Amsterdam Office" --set name="AMS Office"--scaffold and --set are available on 114 of the 117 Classic create/update/apply commands, across 44 of the 45 Classic resources carrying those verbs. The exception, classic-computer-configs, binds no request-body schema; it takes --from-file and stdin, and is refused on a gateway profile.
--help on these commands lists the required fields, the optional top-level sections, each enum's allowed values, and which fields carry credentials.
A Classic body can carry a credential, so put it in a file, never in a flag value or in
argv.
Classic --set builds the entire body, so it is mutually exclusive with --from-file. (The Platform and Security Cloud --set overlays onto a --from-file body instead.)
--set and --from-file are mutually exclusive; --set builds the whole body, so pass the file
alone or set fields alone
A Classic PUT is a partial update: omitted fields keep their values, so --set alone is a valid update. A body carrying only <name> renames a network segment and leaves its address range, its override flags and a group's whole criteria array intact.
Piping XML to stdin and passing --set warns instead of failing (warning: --set builds the whole body; ignoring stdin), because a CI runner hands every process an empty stdin.
The Classic API hides a malformed body. Wire-checked:
| You send | The API answers |
|---|---|
| An element the schema does not declare |
201, and the server drops the element |
| A value outside an enum |
201, and the field reads back as the default (frequency: "Twice per fortnight" becomes Once per computer) |
| A missing required field |
409, with the field named inside an HTML error page, one field per response |
--set therefore refuses an unknown field and an out-of-enum value, each refusal exiting 1. --help renders an Allowed values: block per enum. A [] suffix on a dotted path there means each element of an array is constrained: account_maintenance.accounts[].action: Create | Reset | Delete | DisableFileVault on classic-policies create.
--set bogus: network_segment_post declares no such field. Settable here: building, department, ...
--set general.frequency: "Twice per fortnight" is not one of Once per computer,
Once per user per computer, ... (the Classic API accepts an out-of-range value and
silently substitutes its default, so this is refused here)
An empty value for an enum field is refused too. Without the refusal, --set general.frequency="$FREQ" with FREQ unset sends <frequency></frequency>, which the API answers 200 while resetting the policy's execution frequency to Once per computer:
$ jamf-cli pro classic-policies create --set general.name=X --set general.frequency=
--set general.frequency: an empty value is not one of Once per computer, Once per user per
computer, Once per user, Once every day, Once every week, Once every month, Ongoing; omit
the flag to leave the field unchanged (a shell variable that expanded to nothing is the
usual cause)
A non-enum field accepts key= and renders an empty element, so you can clear a field.
--set refuses a credential field (exit 1), and shell completion omits credential fields:
$ jamf-cli pro classic-directory-bindings create --set name=X --set password=hunter2
--set password: password carries a credential and cannot be passed as a flag value, where it
would land in shell history, ps output and CI logs; put the whole body in a file and use
--from-file
A secret the schema does not declare gets the unknown-field refusal instead:
$ jamf-cli pro classic-distribution-points create --set name=X --set ws_password=hunter2
--set ws_password: distribution_point_post declares no such field. Settable here:
connection_type, context, enable_load_balancing, ... Run --scaffold for the whole shape,
or use --from-file for a field the spec does not know
Affected resources include distribution points, SMTP servers, LDAP servers, directory bindings, VPP accounts, disk-encryption configurations (the institutional FileVault recovery keystore and its key material included) and JWT configurations (the signing key). Each command's --help lists its own under Credential fields (--from-file only, never --set).
jamf-cli pro classic-policies create --scaffold fills each optional section with one specimen entry. Those specimens reference objects that do not exist on your instance:
-
general.category.idrenders the spec's example0, which answers409 No match found for category 0. -
scopeandaccount_maintenanceanswer 500.
Delete the sections you do not need before sending.
jamf-cli pro classic-policies list -o table # List all policies
jamf-cli pro classic-policies get 100 # Get policy by ID
jamf-cli pro classic-policies get --name "Deploy Chrome" # Get by name
# Apply (upsert): create or replace by name
cat policy.xml | jamf-cli pro classic-policies apply --yes
# Delete by name
jamf-cli pro classic-policies delete --name "Legacy Policy" --yesjamf-cli pro classic-macos-config-profiles list # macOS config profiles
jamf-cli pro classic-mobile-config-profiles list # Mobile config profiles
jamf-cli pro classic-macos-config-profiles get 22 # Get by IDUse --mobileconfig-file to inject a .mobileconfig file into the profile payload on create, update, or apply:
# Apply (upsert) a macOS config profile: create or replace by name
cat > profile-meta.xml <<EOF
<os_x_configuration_profile>
<general>
<name>Wi-Fi Settings</name>
<category><name>Network</name></category>
</general>
</os_x_configuration_profile>
EOF
jamf-cli pro classic-macos-config-profiles apply \
--from-file profile-meta.xml \
--mobileconfig-file /path/to/wifi.mobileconfig \
--yesNote: On
updateandapply(replace), the CLI injects<redeploy_on_update>All</redeploy_on_update>so devices receive the updated profile without a re-enrol. It also preserves the existingPayloadUUIDandPayloadIdentifierso the profile renews cleanly on device.
--custom-payload-file wraps a preference plist (XML or binary) into a com.apple.ManagedClient.preferences payload and injects it as the profile's mobileconfig payload, taking the preference domain from the filename. Available on create, update and apply, and mutually exclusive with --mobileconfig-file.
# Create a profile from a plist; name defaults to the plist filename (without extension)
jamf-cli pro classic-macos-config-profiles create \
--custom-payload-file /path/to/com.example.app.plist
# Override the profile name
jamf-cli pro classic-macos-config-profiles apply \
--custom-payload-file /path/to/com.example.app.plist \
--name "Example App Settings" \
--yes
# Override the preference domain (useful when the filename doesn't match the bundle ID)
jamf-cli pro classic-macos-config-profiles apply \
--custom-payload-file /path/to/settings.plist \
--custom-payload-domain com.example.app \
--name "Example App Settings" \
--yes
# Bundle multiple plists into a single profile (domain inferred per file)
jamf-cli pro classic-macos-config-profiles apply \
--custom-payload-file /path/to/com.example.app.plist \
--custom-payload-file /path/to/com.example.other.plist \
--name "Multi-domain Settings" \
--yes| Flag | Description |
|---|---|
--custom-payload-file |
Path to a preference plist (XML or binary). Repeatable. Mutually exclusive with --mobileconfig-file. |
--custom-payload-domain |
Preference domain override. Only valid with exactly one --custom-payload-file; inferred from filename when omitted. |
--name |
Profile name override. Useful when the plist filename is not a meaningful profile name. |
The server entity-decodes a submitted <payloads> value one extra time and rejects the write with HTTP 409 "Unable to update the database" when the result contains a bare & or <. A plist must encode those characters, so a profile carrying <string>R&D</string> fails, as does a get XML body piped back into apply. Jamf PI-827.
The CLI handles the escaping on create, update, and apply for classic-macos-config-profiles and classic-mobile-config-profiles. No flags to set:
# A profile with & or < in a value now uploads instead of returning 409
jamf-cli pro classic-macos-config-profiles apply \
--from-file profile-meta.xml \
--mobileconfig-file /path/to/rnd-settings.mobileconfig \
--yes
# get → apply round-trips (backup restore) work too
jamf-cli pro classic-macos-config-profiles get 22 -o xml > profile.xml
jamf-cli -p target pro classic-macos-config-profiles apply --from-file profile.xml --yesHandled for you:
| Input | Handling |
|---|---|
Any & or < in a payload value |
Escaped to the one form the server accepts, then wrapped in CDATA |
| A GET/backup XML body piped back in | Entity-decoded first, so the true plist is recovered before re-escaping |
A CMS-signed .mobileconfig
|
The inner profile is extracted and uploaded, with a note on stderr, because the Classic API cannot preserve a signature |
A binary plist (bplist00 header) |
Converted to XML |
An embedded ]]> or CDATA section inside the plist |
Rewritten as escaped character data so it cannot terminate the wrapper early |
Post-write verification. After a successful write the CLI re-fetches the stored payload, compares it against what you submitted, and names each differing dotted path on stderr, with the reason, listing at most three and then … and N more:
warning: the server stored 2 payload value(s) differently than submitted:
- PayloadContent[3].LoginwindowText: extra & / < entity layer added by the server (Jamf PI-827) — no wire format stores this payload type faithfully
- PayloadContent[3].AdminHostInfo: extra & / < entity layer added by the server (Jamf PI-827) — no wire format stores this payload type faithfully
No flag changes that warning. Storage behaviour is per payload type: com.apple.ManagedClient.preferences (Custom Settings values and dict keys) and com.apple.notificationsettings store byte-exact; other macOS payload types and all mobile device payloads keep one extra entity layer, so a literal & or < arrives on device as & / <. Where the value matters, use a Custom Settings (MCX) payload or the Platform blueprints API.
The comparison normalises CR and CRLF line endings before diffing, because the server rewrites
\rto\non ingest. U+2028, U+2029 and U+0085 are compared exactly: those round-trip byte-exact, so a difference there is corruption.
Note: The escaping and verification cover configuration profiles only. AppConfig is spec-compliant, so
classic-mobile-apps --appconfig-filegets no escaping (it would corrupt values:A & Bstored asA & B); the signed, binary and]]>handling above still applies.classic-mac-apps --appconfig-fileis accepted and then discarded, a followinggetreturning noapp_configuration: Mac App Store AppConfig is deprecated server-side.
jamf-cli pro classic-packages list -o table # List all packages
jamf-cli pro classic-packages get 50 # Get package by ID
jamf-cli pro classic-packages get --name "Firefox" # Get by nameManage Jamf Pro local accounts and groups via the Classic API. Both subcommands carry full CRUD.
# User accounts
jamf-cli pro classic-account-users list
jamf-cli pro classic-account-users get 10
cat user.xml | jamf-cli pro classic-account-users create
cat user.xml | jamf-cli pro classic-account-users update 10
jamf-cli pro classic-account-users delete 10 --yes
# Local groups
jamf-cli pro classic-account-groups list
jamf-cli pro classic-account-groups get 5
cat group.xml | jamf-cli pro classic-account-groups create
cat group.xml | jamf-cli pro classic-account-groups update 5
jamf-cli pro classic-account-groups delete 5 --yesNote:
classic-accounts listreturns both users and groups in one payload, for the overview case.
classic-computer-groups and classic-mobile-device-groups manage smart and static groups through the Classic API. Each carries list, get, create, update, delete plus name-based apply. get, update and delete take --name in place of a numeric ID.
# Computer groups (smart + static)
jamf-cli pro classic-computer-groups list
jamf-cli pro classic-computer-groups get --name "All Managed Clients"
cat group.xml | jamf-cli pro classic-computer-groups apply --yes # create or replace by name
jamf-cli pro classic-computer-groups delete --name "Old Group" --yes
# Mobile device groups (smart + static)
jamf-cli pro classic-mobile-device-groups list
jamf-cli pro classic-mobile-device-groups get --name "All iPads"
jamf-cli pro classic-mobile-device-groups apply --from-file ipads.xml --yesFour Modern API equivalents return JSON:
computer-groups-smart-groups(aliassmart-computer-groups),computer-groups-static-groups,mobile-device-groups-smart-groupsandmobile-device-groups-static-groups. Use the Classic commands for the XML group payload (smart-group criteria), or alongside other Classic resources.
pro static-computer-groupsis a retiring alias ofpro computer-groups-static-groups(the v3 endpoint the gateway publishes) and is not refused on a gateway profile:pro static-computer-groups listwarns about the name and returns the groups.
classic-advanced-mobile-device-searches manages saved advanced mobile device searches via the Classic API, alongside classic-advanced-computer-searches. Full CRUD plus name-based apply, with --name lookup on get/update/delete.
jamf-cli pro classic-advanced-mobile-device-searches list
jamf-cli pro classic-advanced-mobile-device-searches get --name "Out of Compliance"
jamf-cli pro classic-advanced-mobile-device-searches apply --from-file search.xml --yesServed on a gateway profile.
pro classic-patch-titles listandpro classic-patch-policies listboth return XML at exit 0, andpro classic-patch-reports get <id>reaches Jamf Pro: a missing title answers its own404, exit 4 (the policy refusal would be exit 8).
# Get a specific patch report by software title ID
# (Classic API exposes no list endpoint; fetch a report by ID)
jamf-cli pro classic-patch-reports get 3classic-computer-history and classic-mobile-history fetch a device's Classic API history record: management commands, policy logs, audits, user and location history. Both are get-only, and get looks up a device by id, --name, --serialnumber (alias --serial), --macaddress or --udid.
# Get a computer's full history by ID
jamf-cli pro classic-computer-history get 42
# Look up by serial number (--serial is an alias for --serialnumber)
jamf-cli pro classic-computer-history get --serial C02X1234
# Return only one section of the record, server-side, with --subset (tab-completable)
jamf-cli pro classic-computer-history get --serial C02X1234 --subset Commands
jamf-cli pro classic-mobile-history get --name "Lab iPad 12" --subset ManagementCommands--subset narrows the response to a single section server-side (one value; the API's &-combined multi-subset syntax isn't supported here yet). Tab-complete to see the valid sections:
-
Computer history:
General,ComputerUsageLogs,Audits,PolicyLogs,CasperRemoteLogs,ScreenSharingLogs,CasperImagingLogs,Commands,UserLocation,MacAppStoreApplications -
Mobile device history:
General,ManagementCommands,UserLocation,Audits,Applications,Ebooks
Note: With
--subsetand a non-ID lookup (--serial, say), the CLI resolves the record's ID first, then requests the subset by ID, the only form the Platform Gateway's Classic proxy allows.--serialis also an alias onclassic-mobile-devices get.
classic-computer-app-usage fetches per-day application usage for one computer over a date range. No Modern API equivalent exists, and the Classic endpoint carries its date range in the URL path (/JSSResource/computerapplicationusage/id/{id}/{start}_{end}), so it is one hand-written command with no list/get pair.
Identify the computer with exactly one of --id, --serial, --udid, or --name. Non-ID identifiers are resolved to a computer ID via inventory search first. Supply the range with either --start and --end (yyyy-mm-dd), or --last for a rolling window ending today.
# Fixed range, by ID
jamf-cli pro classic-computer-app-usage --id 42 --start 2026-06-01 --end 2026-06-30
# Rolling window, by serial number
jamf-cli pro classic-computer-app-usage --serial C02XL0ABCDEF --last 30d
# Two weeks, by computer name, as CSV
jamf-cli pro classic-computer-app-usage --name "Neil's MacBook" --last 2w -o csv| Flag | Description |
|---|---|
--id |
Computer ID (used directly, no lookup) |
--serial |
Computer serial number (resolved via inventory search) |
--udid |
Computer UDID |
--name |
Computer name (resolved via inventory search) |
--start |
Range start date, yyyy-mm-dd (use with --end) |
--end |
Range end date, yyyy-mm-dd (use with --start) |
--last |
Rolling window ending today, e.g. 30d or 2w (instead of --start/--end) |
Every output format carries one row per (date, application): date, name, version, foreground, open. foreground is minutes in the foreground, open the launch count, so -o json pipes into jq without walking the API's per-day nesting.
Note: This command negotiates JSON with the server, the only way the endpoint returns its array-shaped body, so its output is JSON and not XML.
app-installers (with its nested global-settings group), app-installers-deployments and app-installers-titles cover every App Installer endpoint: per-deployment computers, installation summaries, history, version updates and retry operations.
The App Installer endpoints are published in the gateway's Jamf Pro API, so these commands work on a platform gateway profile and on an instance profile. The feature probe reports App Installer availability, the Cloud Services Connection state and the environment's capabilities.
jamf-cli pro app-installers get{
"cloudServicesEnabled": true,
"features": [
"CONTROL_DEPLOYMENT_PROCESS",
"M2M_INTEGRATION"
]
}# List all App Installer deployments
jamf-cli pro app-installers-deployments list
# Get a deployment by name
jamf-cli pro app-installers-deployments get --name "Slack"
# Create or replace a deployment
jamf-cli pro app-installers-deployments apply --from-file slack-deployment.json --yes
# Update a deployment's fields in place (fetch-merge-put with --set)
jamf-cli pro app-installers-deployments update --name "Slack" --set <key>=<value>
# Delete a deployment
jamf-cli pro app-installers-deployments delete --name "Slack" --yes
# Export all deployment configurations as CSV
jamf-cli pro app-installers-deployments export --out-file deployments.csv
# List computers in a deployment (by name or ID)
jamf-cli pro app-installers-deployments computers --name "Slack"
# Get installation summary for a deployment
jamf-cli pro app-installers-deployments installation-summary --name "Slack"# Get a deployment's object history (notes + change details, paginated)
jamf-cli pro app-installers-deployments history --name "Slack"
# Filter history with RSQL over username, date, note, details
jamf-cli pro app-installers-deployments history --name "Slack" --filter 'username=="admin"'
# Add a note to a deployment's object history (body piped via stdin)
jamf-cli pro app-installers-deployments add-history-note --name "Slack" --scaffold # show the body template: {"note":""}
echo '{"note":"Approved for production"}' | jamf-cli pro app-installers-deployments add-history-note --name "Slack"# Trigger a version update for a deployment's app title
jamf-cli pro app-installers-deployments version-update --name "Slack"
# Retry all failed installations for one deployment
jamf-cli pro app-installers-deployments installation-retry --name "Slack"
# Retry failed installations across EVERY deployment in a single server-side call
jamf-cli pro app-installers-deployments installation-retry --all --yes
# Retry a failed installation on a single computer within a deployment
# args: <deployment-id> <computerId>
jamf-cli pro app-installers-deployments installation-retry-by-computer-id 12 345Note:
installation-retry --allhits the collection-level retry endpoint, retrying failures across all deployments in one request.--yesskips its confirmation prompt.
# List available App Installer titles
jamf-cli pro app-installers-titles list
# Get a title by ID, or by name (matched against the catalogue's titleName)
jamf-cli pro app-installers-titles get 001
jamf-cli pro app-installers-titles get --name "Adobe Creative Cloud"
# Get a specific version of a title (default: the newest)
jamf-cli pro app-installers-titles get 001 --version 6.5.0
# List the versions available for a title (server-ordered)
jamf-cli pro app-installers-titles versions 001
jamf-cli pro app-installers-titles versions --name "Adobe Creative Cloud"
# Only versions above a floor
jamf-cli pro app-installers-titles versions 001 --start-version 6.0.0
--namematches the catalogue'stitleNamefield. The titles collection declares no server-sidefilter, so the lookup re-fetches and matchestitleNameclient-side. Against a 363-title catalogue,get --name "Adobe Creative Cloud"returns title001withtitleName,bundleId,publisher,iconUrl,versionandinstallationPathShared.
app-installers global-settings is a nested command group on the app-installers singleton, so it takes no IDs; /v1/app-installers/global-settings carries a PUT on itself. See Sub-Resource Command Groups.
The former name pro app-installer-global-settings resolves two tokens deep and warns:
$ jamf-cli pro app-installer-global-settings get
warning: `app-installer-global-settings` is a deprecated name for `app-installers global-settings` and
stops working after 2027-03-09. Use `pro app-installers global-settings`.
# Get the settings object
jamf-cli pro app-installers global-settings get
# Update it (run with --scaffold to see the field paths)
jamf-cli pro app-installers global-settings update --set <key>=<value>
# Read the default Global Deployment Process Controls
# -> {"commandsBatchSize": 10000, "batchFrequencyInMinutes": 15,
# "daysOfWeek": ["MONDAY", "TUESDAY", ...], ...}
jamf-cli pro app-installers global-settings deployment-controls
# Object history for the settings (paginated; RSQL over username, date, note, details)
jamf-cli pro app-installers global-settings history
jamf-cli pro app-installers global-settings history --filter 'username!=admin'
# Add a note to that history
jamf-cli pro app-installers global-settings add-history-note --scaffold # {"note":""}
echo '{"note":"Enabled cloud services"}' | jamf-cli pro app-installers global-settings add-history-noteNone of these are stated in the spec.
-
An identifier must be a positive numeric string or
-1, and the server checks the shape before the lookup.app-installers-deployments get 0answers400 INVALID_ID(id field must be string of positive numeric value or -1), not404. -
-1is the no-assignment sentinel. An omittedsmartGroupIdreads back as"-1". The same holds forcategoryIdandsiteId. -
A create's
hrefis not callable through the gateway. The server builds it from the Jamf Pro instance hostname with an/apiprefix, and the gateway base URL carries neither. Take theidfrom the response and build your own path. -
Both retries answer
404with an emptyerrorsarray when there is nothing to retry, on a deployment thatgetanswers200for in the same run. That is Jamf Pro's own response.
pro jamf-pro-notifications (alias notifications) reads and dismisses the alerts Jamf Pro raises about the instance.
# Every current notification
jamf-cli pro jamf-pro-notifications list
# Dismiss one, by id and type
jamf-cli pro jamf-pro-notifications delete 1 2 --yes
# Dismiss every dismissible notification in one call
jamf-cli pro jamf-pro-notifications delete --all --yes--all (v1.30.0, Jamf Pro 11.32.0) sends DELETE /v1/notifications, which dismisses every dismissible notification server-side in one request. It confirms first unless --yes, and it refuses to be combined with a positional: dismissing one notification and dismissing the lot are different endpoints, not one endpoint with a wider target. The per-notification delete <id> <type> signature is unchanged; the Use string now reads [<id>] [<type>] because with --all both are optional.
-n / --dry-run previews the --all request without sending it. In v1.29.0 it did not: delete --all --yes -n sent a live tenant-wide DELETE and got a 204, wire-checked before and after the fix. A destructive generated command declares its own --dry-run, which shadows the root persistent flag, and the template's own check sat after the --all block had already sent the request. The preview now runs before both the confirmation and the request. --all was also unreachable before v1.30.0 for a second reason: cobra validates Args before RunE, so the emitted ExactArgs refused the invocation before the flag could be read.
The other collection-level --all in the pro namespace is pro app-installers-deployments installation-retry --all; it was never affected by the --dry-run defect, because it is not destructive and declares no local --dry-run.
Nine Classic API resources carry a scope subcommand with get, add and remove, so you read and change scope without writing XML: classic-policies, classic-macos-config-profiles, classic-mobile-config-profiles, classic-mac-apps, classic-mobile-apps, classic-ebooks, classic-restricted-software, classic-vpp-assignments, classic-vpp-invitations.
scope get, scope add and scope remove now take an optional [<id>], with --name as the alternative, matching every other Classic command. A v1.29.0 script that passes a bare name is broken: the name is refused, not resolved.
# v1.29.0
jamf-cli pro classic-policies scope get "Deploy Chrome"
jamf-cli pro classic-policies scope add "Deploy Chrome" --computer-group "All Managed Clients"
# v1.30.0
jamf-cli pro classic-policies scope get 1
jamf-cli pro classic-policies scope get --name "Deploy Chrome"
jamf-cli pro classic-policies scope add 1 --computer-group "All Managed Clients"
jamf-cli pro classic-policies scope add --name "Deploy Chrome" --section exclusion --building "London"
jamf-cli pro classic-policies scope remove --name "Deploy Chrome" --computer-group "Test Group"scope get takes no category flags, only --name, and goes through the shared formatter, so every -o value works on it:
jamf-cli pro classic-macos-config-profiles scope get --name "Wi-Fi Settings" -o table
jamf-cli pro classic-macos-config-profiles scope get 12 -o yamlA positional and --name together are refused: pass either an <id> argument or --name, not both. A non-numeric positional exits 2 and names the replacement:
$ jamf-cli pro classic-policies scope get "Deploy Chrome"
message: "Deploy Chrome" is not an id; Classic ids are numeric
hint: pass a name as --name "Deploy Chrome" — the positional argument used to be the name
on these commands and is now the id
A --name matching more than one record is refused rather than resolved to the first in document order. Classic names are not unique; a live tenant carried two ebooks sharing one.
Each scope command registers only the categories its own resource can hold, so a cross-family flag no longer exists on it. It arrives as cobra's unknown-flag error, and the hint names what the resource does accept:
$ jamf-cli pro classic-policies scope add 1 --class "Year 9"
message: unknown flag: --class
hint: scope categories for this resource: --building, --computer, --computer-group,
--department, --ibeacon, --jss-user, --jss-user-group, --network-segment, --user
or --user-group
| Resource | Categories | Sections |
|---|---|---|
classic-policies |
--building --computer --computer-group --department --ibeacon --jss-user --jss-user-group --network-segment --user --user-group
|
target, limitation, exclusion |
classic-macos-config-profiles |
as classic-policies
|
target, limitation, exclusion |
classic-mac-apps |
as classic-policies, minus --ibeacon
|
target, limitation, exclusion |
classic-mobile-config-profiles |
--building --department --ibeacon --jss-user --jss-user-group --mobile-device --mobile-device-group --network-segment --user --user-group
|
target, limitation, exclusion |
classic-mobile-apps |
as classic-mobile-config-profiles, minus --ibeacon
|
target, limitation, exclusion |
classic-ebooks |
both families: --building --class --computer --computer-group --department --jss-user --jss-user-group --mobile-device --mobile-device-group --network-segment --user --user-group
|
target, limitation, exclusion |
classic-restricted-software |
--building --computer --computer-group --department --user
|
target, exclusion (no limitation section at all) |
classic-vpp-assignments |
--jss-user --jss-user-group --user-group
|
target, limitation, exclusion |
classic-vpp-invitations |
--jss-user --jss-user-group --user-group
|
target, limitation, exclusion |
Which sections a category is valid in does not vary by resource, with one exception:
| Category | Valid sections | Notes |
|---|---|---|
--building, --computer, --computer-group, --department, --jss-user, --jss-user-group, --mobile-device, --mobile-device-group
|
target or exclusion |
--jss-user is a Jamf Pro user name; --jss-user-group a Jamf Pro user group name |
--ibeacon, --network-segment, --user, --user-group
|
limitation or exclusion |
--user is a directory or local username, free text, not a Jamf Pro user; --user-group is a directory (LDAP/IdP) user group name |
--class (classic-ebooks only) |
target | |
--user on classic-restricted-software
|
exclusion | the exception; the other four categories there are target or exclusion |
--ibeacon (policies, macOS profiles, mobile profiles) and --class (ebooks) are new in v1.30.0. Both were readable through scope get and writable by nothing. --user is now accepted as a classic-restricted-software exclusion, where it was refused outright while the wire stored it.
--computer and --mobile-device values are resolved by format: a 40-character hex string as a UDID, a plain integer as a numeric Jamf Pro ID, anything else as a device name. Serial numbers are not resolved here; use a name, UDID or numeric ID.
Jamf Pro stores <classes> only while the stored category is empty. A write made while it already holds a member clears it, and carrying the identical value, omitting the element, and every identifier shape all clear it alike. Since a scope PUT replaces <scope> wholesale, no single request can preserve an existing class across any other scope change: scope add --building on an ebook holding a class used to destroy the class and report success.
So the CLI sends the intended change with <classes> emptied, then the same scope with them populated. It is keyed on the category rather than the resource, and the first request carries the change you asked for, so an interruption leaves you no worse off than the single request it replaced. Two PUTs go out per --class write, which matters if you are counting requests or handling a partial failure.
The request body carries nothing but <scope>. A Classic PUT is a partial update at top-level-section granularity: wire-verified against both a direct instance and the gateway, every non-scope byte comes back identical, a 19 KB configuration profile's <payloads> included. <scope> itself is replaced wholesale, which is why the current scope is read first. One scope add used to cost three GETs and a PUT re-sending the whole document.
After the write the CLI re-fetches and diffs the whole scope it sent against what came back, naming anything missing, matching on name, id or udid because the server augments what it was sent. It previously verified only the category the command touched, so a change the server dropped elsewhere in the scope went unreported.
-n / --dry-run works. It used to fail every time: the preview suppressed the PUT and the check then reported that the server had not persisted it.
Both operations are idempotent. Adding an item that is already present, or removing one that is absent, prints a message to stderr and returns success.
A Classic HTTP error now renders the one useful sentence from its HTML status page instead of ~400 bytes of markup, which is how Error: Unable to match computer group and Error: Duplicate name became readable.
<limit_to_users> and a dead limitations.computer_groups field are gone from the body. The server denormalises <limitations><user_groups> into <limit_to_users> on every write and back on every read, verified in both directions.
If you write your own
<scope>XML rather than using these commands, element order is load-bearing. The Classic XML binding is sequence-ordered: it reads scope children in schema order and silently ignores whatever arrives out of it, answering 200 either way. The order a GET returns is not schema order and differs per resource.
A sub-path that carries PUT, PATCH or DELETE on itself is a separately-writable object and gets a command group of its own, with its own plain verbs. Ten qualify:
| Command group | Endpoint |
|---|---|
pro activation-code organization-name |
/v1/activation-code/organization-name |
pro app-installers global-settings |
/v1/app-installers/global-settings |
pro csa token |
/v1/csa/token |
pro enrollment adue-session-token-settings |
/v1/adue-session-token-settings |
pro local-admin-password settings |
/v2/local-admin-password/settings |
pro managed-software-updates-plans feature-toggle |
/v1/managed-software-updates/plans/feature-toggle |
pro self-service settings |
/v1/self-service/settings |
pro self-service-plus settings |
/v1/self-service-plus/settings |
pro sso-settings cert |
/v2/sso/cert |
pro sso-settings oidc-broker-config |
/v3/sso/oidc-broker-config |
jamf-cli pro csa token get
jamf-cli pro local-admin-password settings get
jamf-cli pro self-service settings get
jamf-cli pro managed-software-updates-plans feature-toggle get
jamf-cli pro enrollment adue-session-token-settings getpro sso-settings is the SSO configuration (/v3/sso). The certificate and the OIDC broker configuration are groups beneath it:
| Command | Sends | Returns |
|---|---|---|
pro sso-settings get / update
|
GET/PUT /v3/sso
|
the SSO configuration |
pro sso-settings download |
GET /v3/sso/metadata/download |
the Jamf Pro SAML metadata file |
pro sso-settings cert get / create / update / delete
|
/v2/sso/cert |
the SSO certificate |
pro sso-settings cert download |
GET /v2/sso/cert/download |
the certificate keystore |
pro sso-settings cert parse |
POST /v2/sso/cert/parse |
a parsed keystore, without storing it |
pro sso-settings oidc-broker-config get / update
|
GET/PUT /v3/sso/oidc-broker-config
|
the tenant's selected OIDC broker configuration |
pro sso-settings failover |
GET /v1/sso/failover |
the failover URL |
pro sso-settings delete no longer exists. To delete the SSO certificate, run pro sso-settings cert delete.
oidc-broker-config is new in Jamf Pro 11.32.0, so it did not exist under any earlier spelling. get never returns secret fields. update is a full replacement: pipe a full JSON document to stdin, or set individual fields with --set (fetch-merge-replace). --scaffold prints the field template. Omit clientSecret and privateKeyJwt to keep the stored value, and supply either to rotate it. Changing clientAuthMethod in the same request requires the new method's credential, because the authentication service clears the credential belonging to the method being left.
jamf-cli pro sso-settings oidc-broker-config get
jamf-cli pro sso-settings oidc-broker-config update --scaffold
jamf-cli pro sso-settings oidc-broker-config update --set enabled=truepro csa token, pro local-admin-password settings and pro managed-software-updates-plans feature-toggle are command groups: a bare invocation prints help and exits 0, and one asking for data is refused:
$ jamf-cli pro csa token -o json
{
"error": "usage",
"exitCode": 2,
"exitCodeName": "usage",
"hint": "run `jamf-cli pro csa token get`",
"message": "`pro csa token` is a command group and returns no data; it returned data before the sub-resource split"
}
Exit 2. The refusal fires on -o/--output, --field, --select and --out-file. A bare invocation, and a typo beneath it, are unaffected.
The resource's own root keeps create, update and delete. A sub-path's write is qualified by the segment that owns it. 19 such operations ship.
# The customization's own CRUD: POST /v2/enrollment-customizations
cat customization.json | jamf-cli pro enrollment-customization create
# The panel writes
jamf-cli pro enrollment-customization ldap-create <id> # POST /v1/enrollment-customization/{id}/ldap
jamf-cli pro enrollment-customization ldap-update <id> <panel-id>
jamf-cli pro enrollment-customization ldap-delete <id> <panel-id>
jamf-cli pro enrollment-customization sso-create <id> # sso-update / sso-delete likewise
jamf-cli pro enrollment-customization text-create <id> # text-update / text-delete likewise
jamf-cli pro enrollment-customization all-delete <id> <panel-id>| Command | Endpoint |
|---|---|
pro computer-inventory attachments-delete <id> <attachmentId> |
DELETE /v4/computers-inventory/{id}/attachments/{attachmentId} |
pro packages manifest-delete [<id>] |
DELETE /v1/packages/{id}/manifest |
pro venafi proxy-trust-store-create / proxy-trust-store-delete <id>
|
POST/DELETE /v1/pki/venafi/{id}/proxy-trust-store
|
pro patch-software-title-configurations dashboard-delete [<id>] |
DELETE /v3/patch-software-title-configurations/{id}/dashboard |
pro computer-prestages create-scope / update-scope
|
POST/PUT /v2/computer-prestages/{id}/scope
|
pro mobile-device-prestages create-scope / update-scope
|
POST/PUT /v2/mobile-device-prestages/{id}/scope
|
Each path above is wire-confirmed with -v. pro venafi proxy-trust-store-create cannot succeed: it sends application/json where the endpoint declares application/pem-certificate-chain, so it answers 415 Content-Type 'application/json' is not supported before reading the ID. The read (proxy-trust-store) and the delete work; upload the chain through Jamf Pro's own UI until the content type is fixed.
pro enrollment-customization-panels create/update/deleteare refused, and the refusal names the panel command to run instead:$ jamf-cli pro enrollment-customization-panels update 1 2 message: `pro enrollment-customization-panels update` no longer names this operation: `enrollment-customization-panels` and `enrollment-customization` are one resource now, and its plain verbs are the resource's own hint: run `jamf-cli pro enrollment-customization ldap-update`Exit 2. The panel reads under that spelling (
all,ldap,sso,text,markdown,parse-markdown) still work.
--scaffold prints the request-body template and sends nothing, so it needs none of the positionals the command declares. The declared ceiling still applies: a scaffold carrying too many positionals is refused (accepts at most 2 arg(s), received 3).
jamf-cli pro enrollment-customization ldap-update --scaffold
jamf-cli pro venafi patch --scaffoldpro device aggregates one device's record into a single view: identity, hardware, OS, security posture, user info, MDM command history and policy execution logs. Identify the device by Jamf Pro ID, serial number or name; resolution cascades ID → serial → name.
jamf-cli pro device C02X1234 # By serial number
jamf-cli pro device 42 # By Jamf ID
jamf-cli pro device "Neil's MacBook" # By name
jamf-cli pro device C02X1234 -o json # JSON output (alias: dev)The CLI fetches MDM and policy history in parallel. Under platform gateway auth the report adds blueprint assignments, compliance benchmark status and DDM declaration state. On a partial failure (missing Classic API privileges, say) it warns on stderr and finishes the report.
MDM command history and policy execution rows carry a completion timestamp (
dateCompleted) alongside status.
pro report subcommands aggregate Jamf Pro data into operational summaries and default to table output.
| Subcommand | Description |
|---|---|
security |
Fleet security posture: FileVault, Gatekeeper, SIP, firewall, OS distribution |
patch-status |
Patch compliance percentages per title; --scan-failures for per-policy failure detail |
device-compliance |
Devices with stale check-ins or outdated OS versions |
inventory-summary |
Hardware model and OS version breakdown |
software-installs |
Installed software version distribution; --title filters, --bundle-id and --path add a column and group by it |
ea-results |
Extension attribute results across devices |
policy-status |
Policy execution health: config findings, failure rates, per-device failure counts |
profile-status |
Configuration profile deployment health |
app-status |
Managed app deployment health |
update-status |
Managed software update deployment status and plan state summary; --scan-failures for per-device failure detail |
# Security posture
jamf-cli pro report security
jamf-cli pro report security -o json
# Patch compliance
jamf-cli pro report patch-status
jamf-cli pro report patch-status --scan-failures # include per-policy failure detail
# Policy health (config issues + failure rates)
jamf-cli pro report policy-status
# Managed software update status (fast: summary tables only)
jamf-cli pro report update-status
# Include per-device failure details (API-expensive)
jamf-cli pro report update-status --scan-failures
# Cap enrichment to 50 devices/plans (random sample)
jamf-cli pro report update-status --scan-failures --limit 50
# Profile and app deployment health
jamf-cli pro report profile-status
jamf-cli pro report app-status
# Fleet compliance and inventory
jamf-cli pro report device-compliance
jamf-cli pro report inventory-summary
jamf-cli pro report software-installs
jamf-cli pro report ea-results- Security Summary: encryption rate, Gatekeeper/SIP/firewall compliance percentages
- Flagged Devices: devices with security gaps (missing FileVault, firewall off, etc.)
- OS Version Distribution: count and percentage per OS version
| Flag | Description |
|---|---|
--title |
Filter to application names containing this substring (case-insensitive) |
--include-system |
Include system apps from /System/ and /Library/ (excluded by default) |
--bundle-id |
Add a bundle_id column and group by bundle ID |
--path |
Add a path column and group by install path |
Default columns are title, version, device_count. --bundle-id and --path each add a column and join the grouping, so one title and version spanning several bundle IDs or install paths reports a row per value.
jamf-cli pro report software-installs --title Safari -o csv
# device_count,title,version
# 1,Safari.app,26.0.1
# 2,Safari.app,17.5
jamf-cli pro report software-installs --title Safari --bundle-id --path -o csv
# bundle_id,device_count,path,title,version
# com.apple.Safari,1,/Applications/Safari.app,Safari.app,26.0.1
# com.apple.Safari,2,/Applications/Safari.app,Safari.app,17.5Without these flags, two apps sharing a display name (a relocated copy, or a repackaged vendor app under a different bundle ID) merge into one row.
| Flag | Description |
|---|---|
--scan-failures |
Also fetch patch policy failure counts and device-level failure detail |
| Flag | Default | Description |
|---|---|---|
--scan-failures |
false | Enrich error devices and failed plans with inventory details and plan events (API-expensive: fetches full computer and mobile inventory, plus one events call per failed plan) |
--limit |
smart | Max failures to enrich per table. -1 (default): max(10%, 100); 0: all; N: explicit cap (random sample) |
Without --scan-failures, update-status returns summary tables only and prints a stderr hint if failures exist.
Run report commands through multi and it aggregates output from all instances: summaries are combined, and detail rows from each instance merge into one table with a profile column added. --sequential gives per-instance output.
# Aggregate patch compliance across all MSP instances
jamf-cli multi --filter 'pro-*' -- pro report patch-status
# See each instance's output separately
jamf-cli multi --filter 'pro-*' --sequential -- pro report securitygroup-tools inspects computer groups: filtering, membership listing, unused-group detection and export.
# List all computer groups with optional filters
jamf-cli pro group-tools list
jamf-cli pro group-tools list --type smart # Only smart groups
jamf-cli pro group-tools list --type static # Only static groups
jamf-cli pro group-tools list --empty # Groups with zero members
jamf-cli pro group-tools list --name-pattern "test" # Case-insensitive name filter
# Show members of a computer group by name
jamf-cli pro group-tools members "All Managed Clients"
jamf-cli pro group-tools members "All Managed Clients" -o csv
# Analyze computer groups for hygiene issues
jamf-cli pro group-tools analyze
jamf-cli pro group-tools analyze --unused # Unreferenced groups that also hold no members
# Export all computer group definitions
jamf-cli pro group-tools export
jamf-cli pro group-tools export --format yaml --out-file groups.yamlEvery group reported 0 members before v1.31.1. Earlier releases read a field
/v1/computer-groupsdoes not return, solist --emptylisted the whole instance andpro auditnever emitted its Empty smart groups finding on any tenant. The counts now come from the two v3 collections that carry one.A count the CLI could not read reads
unknown, not0.--emptyand--unusedtake only the groups proved empty and report on stderr how many they skipped.--unusednames removal candidates, so a group holding members no longer appears in it.
Device action commands live under pro computers (the computer-inventory resource) and pro mobile-devices. They target devices by serial number, name, ID, group, or file. Destructive operations sit behind extra confirmation flags.
A destructive generated action sends its own HTTP method and quotes its own action name. A bulk
pro mobile-device-groups erase --from-file ids.txtsendsPOST /api/v2/mobile-device-groups/{id}/erase, wire-confirmed. ADELETEprompt readsThis will delete …; any other action quotes its own name:$ jamf-cli pro mobile-device-groups erase 67 ⚠️ This will run "erase" on mobile-device-group 67. Type 'yes' to confirm: $ jamf-cli pro mobile-device-groups erase --from-file ids.txt ⚠️ This will run "erase" on 2 mobile-device-groups. Type 'yes' to confirm: $ jamf-cli pro mobile-device-groups erase --from-file ids.txt --dry-run [dry-run] Would run "erase" on mobile-device-group "67" (id: 67) [dry-run] Would run "erase" on mobile-device-group "66" (id: 66)The
--from-fileand--grouphelp text matches. The hand-writtenpro computers/pro mobile-devicesactions below quote their own operation too:⚠️ This will remove-mdm computer "FVFC41HCLYWP" (serial: FVFC41HCLYWP, id: 5).
Most MDM device actions are refused on a platform gateway profile, exit code 8. They go through
POST /v2/mdm/commands, which the gateway's published Jamf Pro API omits:GETis declared on that path,POSTis not. Add a secondoauth2profile pointed at the Jamf Pro instance for them.
Refused on a gateway profile Still served pro computerslock,restart,shutdown,enable-remote-desktop,disable-remote-desktop,set-recovery-lock,set-auto-admin-password,settingsblank-push,redeploy-framework,ddm-sync,renew-mdm,remove-mdm,erase,flush-commandspro mobile-deviceslock,restart,shutdown,enable-lost-mode,disable-lost-mode,play-lost-mode-sound,clear-passcode,clear-restrictions-password,delete-user,log-out-user,unlock-user-account,apply-redemption-code,refresh-cellular-plans,request-mirroring,stop-mirroring,settingserase,unmanage,update-inventory,flush-commands
eraseandremove-mdmon computers are hand-written commands on a published path, so they survive.pro mdm commands, the generic command-issuing endpoint, is refused.
# Send a blank push to trigger check-in
jamf-cli pro computers blank-push --serial C02X1234
# Redeploy the Jamf management framework
jamf-cli pro computers redeploy-framework --serial C02X1234
# Force a Declarative Device Management sync
jamf-cli pro computers ddm-sync --serial C02X1234
# Renew the MDM profile
jamf-cli pro computers renew-mdm --serial C02X1234
# Remove the MDM profile (destructive)
jamf-cli pro computers remove-mdm --serial C02X1234 --yes
# Erase a computer (destructive)
jamf-cli pro computers erase --serial C02X1234 --yes
# Erase with a Find My PIN (from a JSON body file)
jamf-cli pro computers erase --serial C02X1234 --yes --body-file erase-body.json
# Print the JSON template for erase body
jamf-cli pro computers erase --scaffold
# Lock a computer (destructive)
jamf-cli pro computers lock --serial C02X1234 --yes --confirm-destructive
# Enable/disable Remote Desktop
jamf-cli pro computers enable-remote-desktop --serial C02X1234 --yes
jamf-cli pro computers disable-remote-desktop --serial C02X1234 --yes
# Restart a computer
jamf-cli pro computers restart --serial C02X1234 --yes
# Shut down a computer
jamf-cli pro computers shutdown --serial C02X1234 --yes
# Set or clear the Recovery Lock password (Apple Silicon / T2)
jamf-cli pro computers set-recovery-lock --serial C02X1234 --yes
# Flush failed MDM commands (default: leaves pending commands in the queue)
jamf-cli pro computers flush-commands --serial C02X1234 --yes
# Flush both pending and failed commands
jamf-cli pro computers flush-commands --serial C02X1234 --status both --yes
# Flush failed commands from all members of a group
jamf-cli pro computers flush-commands --group "Problem Devices" --yes
# Preview without executing
jamf-cli pro computers flush-commands --serial C02X1234 --dry-run# Erase a mobile device (destructive)
jamf-cli pro mobile-devices erase --serial F4K3SER1AL --yes
# Unmanage a mobile device (destructive)
jamf-cli pro mobile-devices unmanage --serial F4K3SER1AL --yes
# Restart a mobile device
jamf-cli pro mobile-devices restart --serial F4K3SER1AL --yes
# Shut down a mobile device
jamf-cli pro mobile-devices shutdown --serial F4K3SER1AL --yes
# Request an inventory update (Classic API; no modern equivalent)
jamf-cli pro mobile-devices update-inventory --serial F4K3SER1AL --yes
# Lock a mobile device (destructive)
jamf-cli pro mobile-devices lock --serial F4K3SER1AL --yes --confirm-destructive
# Clear the passcode
jamf-cli pro mobile-devices clear-passcode --serial F4K3SER1AL --yes
# Enable/disable Lost Mode
jamf-cli pro mobile-devices enable-lost-mode --serial F4K3SER1AL --yes
jamf-cli pro mobile-devices disable-lost-mode --serial F4K3SER1AL --yes
# Play the Lost Mode sound
jamf-cli pro mobile-devices play-lost-mode-sound --serial F4K3SER1AL --yes
# Clear the restrictions password
jamf-cli pro mobile-devices clear-restrictions-password --serial F4K3SER1AL --yes
# Flush failed MDM commands (default: leaves pending commands in the queue)
jamf-cli pro mobile-devices flush-commands --serial F4GH5678 --yes
# Flush both pending and failed commands
jamf-cli pro mobile-devices flush-commands --serial F4GH5678 --status both --yes
# Flush failed commands from all members of a group
jamf-cli pro mobile-devices flush-commands --group "Problem iPads" --yes
# Preview without executing
jamf-cli pro mobile-devices flush-commands --serial F4GH5678 --dry-runExactly one targeting flag is required. They are mutually exclusive.
| Flag | Description |
|---|---|
--serial |
Target a single device by serial number |
--name |
Target a single device by name |
--id |
Target a single device by Jamf Pro ID |
--group |
Target all members of a smart or static group |
--from-file |
Target multiple devices from a file (one serial or ID per line; blank lines and # comments ignored) |
--group and --from-file enable bulk mode. The CLI lists every resolved device before executing.
# Blank push to all members of a group
jamf-cli pro computers blank-push --group "All Macs" --yes
# Redeploy framework from a file of serials
jamf-cli pro computers redeploy-framework --from-file devices.txt --yesA
--from-fileline resolving to no device is named on stderr, skipped, and counted as a failure in the summary and the exit code;--allow-partial-failuredowngrades that to a warning. A file where nothing resolves fails. Lookups batch at roughly one request per 100 entries.
Destructive actions (erase, remove-mdm, unmanage) in bulk mode require both --yes and --confirm-destructive:
# Blocked: --confirm-destructive is required for bulk erase
jamf-cli pro computers erase --group "Decommissioned" --yes
# ERROR: --confirm-destructive is required for bulk destructive operations
# This works
jamf-cli pro computers erase --group "Decommissioned" --yes --confirm-destructive
# Unmanage retired iPads
jamf-cli pro mobile-devices unmanage --group "Retired iPads" --yes --confirm-destructiveflush-commands removes pending and/or failed MDM commands from a device's queue. Available on both computers and mobile-devices.
| Flag | Description |
|---|---|
--status |
Commands to flush: failed (default), pending, or both
|
--yes |
Skip confirmation prompt (required when --no-input is set) |
-n, --dry-run
|
Preview without executing |
Targeting accepts --serial, --name, --id, or --group. --from-file is not supported: flush targets one device or one group per call.
--status failed (the default) removes stuck commands and leaves unattempted commands in the queue. Use pending or both only to clear the whole queue.
Group flush requires --yes and offers no interactive prompt.
In CI/CD (--no-input), --yes is required:
jamf-cli pro computers flush-commands --serial C02X1234 --no-input --yesJamf Protect deployment tasks are the per-computer install and verification records Jamf Pro creates when a Jamf Protect plan (a configuration profile) deploys to devices through the tenant's Cloud Services Connection.
Plans and deployment tasks are one resource,
pro jamf-protect(aliasjp).pro jamf-protect-plansandpro jamf-protect-deployment-tasksare retiring aliases of it. Its verbs:list(plans),get,create,update,delete,sync,tasks,retry,retry-failed,history,add-history-note.
# Find the deployment UUID for a Protect plan (the "uuid" field)
jamf-cli pro jamf-protect list
# List raw deployment tasks for that deployment, all statuses
jamf-cli pro jamf-protect tasks <deployment-uuid>retry-failed retries failed install tasks for a deployment, resolving a target device (or the whole deployment) to task IDs for you. The generated pro jamf-protect retry <deployment-id> takes task IDs directly and does no lookup.
# Retry the failed task for one computer, by serial number
jamf-cli pro jamf-protect retry-failed <deployment-uuid> --serial C02X1234ABCD
# Retry by management ID or UDID
jamf-cli pro jamf-protect retry-failed <deployment-uuid> --management-id 6f2c1e3a-...
jamf-cli pro jamf-protect retry-failed <deployment-uuid> --udid 00008030-001A2D...
# Retry every failed task in the deployment (requires --yes)
jamf-cli pro jamf-protect retry-failed <deployment-uuid> --all-failed --yes
# Retry specific deployment task IDs directly, skipping lookup
jamf-cli pro jamf-protect retry-failed <deployment-uuid> --task-ids 82,83
# Preview without executing
jamf-cli pro jamf-protect retry-failed <deployment-uuid> --all-failed --dry-run| Flag | Description |
|---|---|
--serial |
Target one computer by serial number |
--management-id |
Target one computer by management ID (UUID) |
--udid |
Target one computer by UDID |
--all-failed |
Retry every failed task in the deployment (requires --yes) |
--task-ids |
Retry specific deployment task IDs directly (advanced; skips lookup) |
--include-succeeded |
With --serial/--management-id/--udid, match that computer's task regardless of status |
Exactly one of --serial, --management-id, --udid, --all-failed, or --task-ids is required. <deployment-uuid> is the Protect plan's deployment UUID (the uuid field from pro jamf-protect list), not the config profile's numeric ID. Only --all-failed requires --yes; single-device targeting and --task-ids run without confirmation.
Note: the Jamf Pro API's
statusfilter on this endpoint returns HTTP 500 for any value, soretry-failedfetches every task for the deployment unfiltered and matches failed tasks (status == GAVE_UP) client-side.
retry-failed --helpstill prints the retired spellings in its description and examples (jamf-cli pro jamf-protect-plans list,jamf-cli pro jamf-protect-deployment-tasks retry-failed …). Those resolve and warn, so the examples run. Use the names above.
jamf-cli multi pro overview # Interactive profile selection
jamf-cli multi --filter 'pro-*' -- pro computers list -o table # Glob filter
jamf-cli multi --profiles a,b,c -- pro overview # Explicit list
jamf-cli multi --from-file instances.txt -- pro computers list # From file (names or URLs)
jamf-cli multi --filter 'pro-*' -- pro report patch-status # Aggregated report across instances
jamf-cli multi --filter 'pro-*' --sequential -- pro overview # Per-instance output (no aggregation)multi aggregates report commands (pro report *); see Report aggregation in multi mode.
pro backup exports configuration objects from a Jamf Pro instance to a local directory, one YAML or JSON file per object, stripping server-generated fields (IDs, timestamps) by default for clean version-control diffs. On a partial failure it records the failed objects in _failures.yaml and carries on.
# Export all resources to a directory
jamf-cli pro backup --output ./jamf-backup
# Export as JSON instead of YAML
jamf-cli pro backup --output ./jamf-backup --format json
# Export a subset of resources
jamf-cli pro backup --output ./jamf-backup --resources policies,scripts
# Retain server-generated IDs in output
jamf-cli pro backup --output ./jamf-backup --include-ids
# Control concurrency (default: 3, ceiling 10)
jamf-cli pro backup --output ./jamf-backup --concurrency 5
# Also download JCDS-hosted package binaries (not just metadata)
jamf-cli pro backup --output ./jamf-backup --download-packages
# List the tokens --resources accepts
jamf-cli pro backup list-resources| Flag | Default | Description |
|---|---|---|
--output |
(required) | Destination directory |
--format |
yaml |
Output format: yaml or json
|
--resources |
(all) | Comma-separated resource filter (e.g., policies,scripts). Tokens: pro backup list-resources. Shell-completes. |
--include-ids |
false | Retain server-generated IDs in output |
--concurrency |
3 |
Max parallel API requests (ceiling 10) |
--download-packages |
false | Also download package binaries hosted on JCDS to packages/files/ (v1.23.0+) |
pro backup list-resources prints every token --resources accepts, on pro backup and on pro diff, from the same table the flag validates against. It needs no credentials and writes nothing to disk.
jamf-cli pro backup list-resources # table: resource, source
jamf-cli pro backup list-resources -o json # adds an `objects` column: the backing commands
jamf-cli pro backup list-resources -o csv --out-file tokens.csv25 tokens as of v1.29.0:
| Token | Source |
|---|---|
accounts |
classic api |
advanced-searches |
classic api, pro api |
blueprints |
platform sdk |
buildings |
pro api |
categories |
pro api |
compliance-benchmarks |
platform sdk |
departments |
pro api |
disk-encryption |
classic api |
dock-items |
classic api |
extension-attributes |
classic api, pro api |
inventory-preloads |
csv download |
mac-apps |
classic api |
mobile-apps |
classic api |
network-segments |
classic api |
packages |
classic api |
patch-titles |
pro api |
policies |
classic api |
prestages |
pro api |
printers |
classic api |
profiles |
classic api |
restricted-software |
classic api |
scripts |
pro api |
sites |
pro api |
smart-groups |
pro api |
static-groups |
pro api |
--output on list-resources is the root format flag; the destination directory pro backup --output takes one level up is local to pro backup. An unrecognised value renders a table, so list-resources --output ./tokens.txt prints the listing and creates no file; use --out-file. --wide changes nothing: objects is not part of the table shape.
advanced-searchescovers both halves in one token: advanced computer searches from the Classic API, advanced mobile device searches from the Pro API. Smart groups are a separate object, captured undersmart-groups.
A filter where nothing matches fails:
$ jamf-cli pro backup --output ./out --resources advanced-search
no resources match filter "advanced-search" # exit 1
An unrecognised token alongside a recognised one is ignored: --resources policies,bogus backs up policies and says nothing about bogus. Check spelling against list-resources, or let completion fill it in.
A stray positional is refused: pro backup /tmp/out exits 2 and writes nothing, the destination directory belonging to --output. A subcommand typo is refused the same way, with a suggestion:
$ jamf-cli pro backup list-resourcez
unknown command "list-resourcez" for "jamf-cli pro backup"
Did you mean this?
list-resources
hint: run jamf-cli pro backup --help to list its subcommands
jamf-cli pro backup --resources inventory-preloads exports inventory preload data as inventory-preloads/inventory-preload-all.csv, downloaded as a single CSV from /v2/inventory-preload/csv.
prestagesbacks up computer and mobile device prestage enrollments and, for each, fetches its per-ID device scope endpoint and embeds the sorted assigned serial numbers under ascopekey, so a prestage's config and its device assignments land in one diff-friendly file.
--download-packagesalso downloads the package binaries backing thepackagesresource, intopackages/files/. Only packages hosted on the Jamf Cloud Distribution Service (JCDS) can be downloaded this way; the backup skips packages on other distribution points (on-prem file share, third-party cloud) with a warning.
diff compares configuration objects between two sources. Each source can be a config profile name (live instance) or a local backup directory.
# Compare two live instances
jamf-cli pro diff --source staging --target production
# Compare a backup to a live instance
jamf-cli pro diff --source ./backup-2026-01 --target production
# Compare two backup directories
jamf-cli pro diff --source ./old-backup --target ./new-backup
# Filter to specific resource types
jamf-cli pro diff --source staging --target production --resources policies,scriptsOutput shows added, removed, and modified resources. Modified entries include the field name, old value, and new value.
Note:
diffneeds no auth when both sources are local backup directories. With either source a profile name, auth resolves normally.
| Flag | Description |
|---|---|
--source |
Source: config profile name or backup directory path (required) |
--target |
Target: config profile name or backup directory path (required) |
--resources |
Comma-separated resource filter (e.g., policies,scripts). Same vocabulary as pro backup: pro backup list-resources. Shell-completes. |
pro difftakes no positionals.pro diff staging productionexits 2 and names the unset flags:"jamf-cli pro diff" takes no positional arguments, but got "staging" (required flag(s) "source", "target" not set).
All generated delete commands support bulk mode via --from-file (a file of IDs, names, or alternate identifiers) or --group (computer-inventory and mobile-devices only).
# Delete computers from a file of serial numbers
jamf-cli pro computer-inventory delete --from-file decommissioned.txt --yes
# Delete all members of a computer group
jamf-cli pro computer-inventory delete --group "Decommissioned Macs" --yes
# Delete all members of a mobile device group
jamf-cli pro mobile-devices delete --group "Retired iPads" --yes
# Delete scripts by name from a file
jamf-cli pro scripts delete --from-file old-scripts.txt --yes
# Preview without executing
jamf-cli pro buildings delete --from-file buildings.txt --dry-run# decommissioned.txt
C02X1234
C02Y5678
42
Neil's MacBook
For the full flag reference, see CLI Patterns#Bulk delete (--from-file and --group).
Note:
mobile-devices deleteuses the Classic API under the hood, because the modern mobile devices API has no DELETE operation.
pro bulk performs bulk mutations across policies, computer groups and MDM commands. It defaults to dry-run: nothing changes without --yes.
Filters are combined with AND logic. Within a repeatable flag, values use OR logic.
# Preview which policies match the filter
jamf-cli pro bulk enable-policies --category Security
# Enable matching policies
jamf-cli pro bulk enable-policies --category Security --yes
# Disable policies matching a name pattern
jamf-cli pro bulk disable-policies --name-pattern "Legacy*" --yes
# Filter by target scope (group, building, department, or all-computers)
jamf-cli pro bulk disable-policies --scope-group "Test Machines" --yes
jamf-cli pro bulk disable-policies --scope-building "London" --yes
jamf-cli pro bulk disable-policies --scope-department "IT" --yes
jamf-cli pro bulk enable-policies --all-computers --yes
# Filter by limitation or exclusion scope
jamf-cli pro bulk enable-policies --limit-network-segment "Corporate" --yes
jamf-cli pro bulk disable-policies --exclude-group "Dev Macs" --yes
# Combine filters: AND logic (must match all)
jamf-cli pro bulk disable-policies \
--category "Legacy" \
--scope-group "Test Machines" \
--exclude-group "Exclude Always" \
--yes| Flag | Section | Description |
|---|---|---|
--scope-group |
Target | Computer group in targets (repeatable) |
--scope-building |
Target | Building in targets (repeatable) |
--scope-department |
Target | Department in targets (repeatable) |
--all-computers |
Target | Scoped to all computers |
--limit-network-segment |
Limitations | Network segment in limitations (repeatable) |
--limit-user-group |
Limitations | User group in limitations (repeatable) |
--exclude-group |
Exclusions | Computer group in exclusions (repeatable) |
--exclude-building |
Exclusions | Building in exclusions (repeatable) |
--exclude-department |
Exclusions | Department in exclusions (repeatable) |
--target-group names the static group to modify. The targets come from either --from-file (one computer ID or serial per line) or --group (the members of another computer group). The two are mutually exclusive.
# Add a list of computers to a static group (dry-run)
jamf-cli pro bulk add-to-group --target-group "Quarantine" --from-file serials.txt
# Execute
jamf-cli pro bulk add-to-group --target-group "Quarantine" --from-file serials.txt --yes
# Use another group's members as the targets
jamf-cli pro bulk add-to-group --target-group "Quarantine" --group "Failed Patch Macs" --yes
# Remove from a static group
jamf-cli pro bulk remove-from-group --target-group "Quarantine" --from-file serials.txt --yesSerials in
--from-fileresolve to computer IDs first. Unresolved lines behave as under Bulk targeting: named on stderr, skipped, counted as failures, tolerated with--allow-partial-failure. When nothing resolves, or the file is empty, the command fails and changes nothing.send-commandbehaves the same.
# Preview: no changes
jamf-cli pro bulk send-command --command BlankPush --group "All Macs"
# Execute
jamf-cli pro bulk send-command --command BlankPush --group "All Macs" --yes
# Destructive command (requires --confirm-destructive)
jamf-cli pro bulk send-command --command EraseDevice --group "Decommissioned" --yes --confirm-destructiveFiles are uploaded in two different ways, depending on the resource.
Most file-carrying resources use a dedicated file flag on create, update and apply. The file's contents populate one body field; supply the metadata (name, category, scope) in the JSON/XML body. The CLI handles encoding, companion fields and name fallback. The file flag overrides a target field already in the body; companion fields and name fallback fill only when absent.
| Resource | Flag | Field populated |
|---|---|---|
scripts |
--script-file |
scriptContents |
computer-extension-attributes |
--script-file |
scriptContents (only for SCRIPT inputType) |
volume-purchasing-locations |
--token-file |
serviceToken (raw; .vpptoken is already base64-encoded JSON). Also works on patch. |
device-enrollments |
--token-file |
encodedToken + auto-fills tokenFileName. Use apply --name <name> --token-file <path> to create or rotate a DEP token in one step; apply composes the token upload and the metadata PUT for you. |
classic-macos-config-profiles |
--mobileconfig-file |
<general><payloads> (CDATA-wrapped; PayloadUUID preserved on update). Or use --custom-payload-file to build the payload from a preference plist; see Configuration Profiles. |
classic-mobile-config-profiles |
--mobileconfig-file |
<general><payloads> (CDATA-wrapped; PayloadUUID preserved on update) |
classic-mac-apps |
--appconfig-file |
<app_configuration><preferences> (fetch-merge-put; other fields untouched) |
classic-mobile-apps |
--appconfig-file |
<app_configuration><preferences> (fetch-merge-put; other fields untouched) |
Prefer apply for file-field workflows: it creates or replaces by name in one call, so you need no exists-check.
# Apply (upsert) a script by name: create or replace
echo '{"name":"Deploy Agent"}' \
| jamf-cli pro scripts apply --script-file /path/to/deploy.sh --yes
# Extension attribute populated from a script file (SCRIPT inputType).
# --scaffold prints the JSON template for the rest of the body.
jamf-cli pro computer-extension-attributes apply --scaffold
# Apply (upsert) a VPP location by name
jamf-cli pro volume-purchasing-locations apply --name "ACME Engineering" \
--token-file /path/to/acme.vpptoken --yes
# Patch a VPP location's token (merge-patch; other fields untouched)
jamf-cli pro volume-purchasing-locations patch --name "ACME Engineering" \
--token-file /path/to/acme.vpptoken
# Create or rotate a DEP server token
jamf-cli pro device-enrollments apply --name "ACME Production" \
--token-file /path/to/server_token.p7m --yes
# Apply (upsert) a macOS configuration profile from a .mobileconfig file.
# On replace, the existing PayloadUUID is preserved so the profile renews cleanly.
cat > profile-meta.xml <<EOF
<os_x_configuration_profile>
<general>
<name>Wi-Fi Settings</name>
<category><name>Network</name></category>
</general>
</os_x_configuration_profile>
EOF
jamf-cli pro classic-macos-config-profiles apply \
--from-file profile-meta.xml \
--mobileconfig-file /path/to/wifi.mobileconfig \
--yes
# Update AppConfig for an existing Mac App by name: scope, category and
# every other attribute are preserved (fetch-merge-put)
jamf-cli pro classic-mac-apps apply \
--name "Slack" \
--appconfig-file /path/to/slack-appconfig.plist \
--yescreate and update are generated for each of these resources. patch changes one field without re-submitting the whole body: rotating a VPP token without re-sending the location name and country code, say.
Resources with a dedicated upload endpoint expose an upload subcommand, which sends a binary file to a separate upload URL.
A
.pkgupload through a platform gateway profile is refused. The gateway sits behind a CDN/WAF that inspects.pkgcontent and blocks it before it reaches Jamf Pro. Upload through an instance profile (auth-method: oauth2ortoken). The CLI reports it as:This is the gateway's CDN/WAF, not Jamf and not your API privileges, so no role change will help. Known triggers: "file://" anywhere in the request body (a legitimate value in some Classic payloads), .pkg upload content, and a burst of writes. The response cannot say which one fired. There is no client-side fix — retry a single request cold, and report it to Jamf.The response is CloudFront's own error page: it names no rule and carries no trace ID. There is no client-side workaround.
# Upload a package file
jamf-cli pro packages upload --file /path/to/app.pkg
# Upload with a custom display name
jamf-cli pro packages upload --file /path/to/app.pkg --name "My App 1.2"
# Upload to a specific category
jamf-cli pro packages upload --file /path/to/app.pkg --category-id 5
# Set installation priority (1–20; default: 10)
jamf-cli pro packages upload --file /path/to/app.pkg --priority 1
# Replace existing package with the same name (skip confirmation)
jamf-cli pro packages upload --file /path/to/app.pkg --yes
# Other file-bearing resources with dedicated upload endpoints
jamf-cli pro icon upload --file /path/to/icon.png
jamf-cli pro inventory-preload upload --file /path/to/preload.csv
jamf-cli pro computer-inventory upload --serial C02X1234 --file /path/to/attachment.pdf
jamf-cli pro computer-extension-attributes upload --file /path/to/script.sh
jamf-cli pro enrollment-customization-images upload --file /path/to/branding.png
jamf-cli pro mobile-device-prestages upload --name "Standard iPad" --file /path/to/image.png
jamf-cli pro self-service upload --file /path/to/logo.pngNote: For scripts and configuration profiles, use the file-based field flags (
--script-file,--mobileconfig-file) oncreate/update/apply; they give you control over each body field (category, scope, priority).pro scripts uploadandpro classic-*-config-profiles uploadno longer exist.
packages sync (also available as jcds sync) mirrors the Jamf Cloud Distribution Service (JCDS) to a local directory. It downloads missing files, re-downloads files whose SHA3-512 checksum has changed, and with --delete removes local files no longer on JCDS. Built for scheduled runs on file-share distribution points.
# Sync to /Volumes/Packages (no local file is deleted)
jamf-cli pro packages sync --dir /Volumes/Packages
# Sync and remove local files not present on JCDS
jamf-cli pro packages sync --dir /Volumes/Packages --delete
# Preview what would change without modifying anything
jamf-cli pro packages sync --dir /Volumes/Packages --delete --dry-run
# Increase parallel download concurrency (default: 4)
jamf-cli pro packages sync --dir /Volumes/Packages --concurrency 8Output is a JSON report with a summary and a files array:
# Show summary counts
jamf-cli pro packages sync --dir /Volumes/Packages | jq '.summary'
# Show only failed files
jamf-cli pro packages sync --dir /Volumes/Packages \
| jq '[.files[] | select(.status == "failed")]'| Flag | Default | Description |
|---|---|---|
--dir |
(required) | Local directory to sync into |
--delete |
false |
Delete local files not present on JCDS |
--dry-run / -n
|
false |
Preview changes without executing |
--concurrency |
4 |
Number of parallel downloads |
File status values in the report: downloaded, updated, skipped, deleted, failed (live run); would-download, would-update, would-delete (dry run).
jcds download retrieves a single file from the Jamf Cloud Distribution Service by fetching a pre-signed URL and streaming it to disk.
# Download by filename (output path defaults to the filename)
jamf-cli pro jcds download MyPackage.pkg
# Download to an explicit output path
jamf-cli pro jcds download MyPackage.pkg --output /tmp/MyPackage.pkg
# Equivalent using --name
jamf-cli pro jcds download --name MyPackage.pkg| Flag | Description |
|---|---|
--name |
Name of the file to download (alternative to positional argument) |
--output / -O
|
Output file path (default: <fileName> in the current directory) |
pro computers settings,pro computers set-auto-admin-passwordandpro mobile-devices settingsare refused on a gateway profile. See the table under Device Actions. They need an instance (oauth2ortoken) profile.
# Configure device settings (bluetooth, timezone, software update cadence)
jamf-cli pro computers settings --serial C02X1234 --yes
# Set the auto admin password (LAPS); password via --password-file or interactive prompt
jamf-cli pro computers set-auto-admin-password --serial C02X1234 --password-file /path/to/pw --yes# Device settings (default browser, bluetooth, device name, roaming, timezone, etc.)
jamf-cli pro mobile-devices settings --serial F4K3SER1AL --yes
# AirPlay mirroring
jamf-cli pro mobile-devices request-mirroring --serial F4K3SER1AL --yes
jamf-cli pro mobile-devices stop-mirroring --serial F4K3SER1AL --yes
# eSIM management
jamf-cli pro mobile-devices refresh-cellular-plans --serial F4K3SER1AL --yes
# App Store
jamf-cli pro mobile-devices apply-redemption-code --serial F4K3SER1AL --yes
# Shared iPad user management
jamf-cli pro mobile-devices delete-user --serial F4K3SER1AL --yes
jamf-cli pro mobile-devices log-out-user --serial F4K3SER1AL --yes
jamf-cli pro mobile-devices unlock-user-account --serial F4K3SER1AL --yesResources with no modern API equivalent are available via the Classic API (/JSSResource/), under the classic- prefix.
Classic resources support list, get, create, update and delete, with --name for name-based lookups and apply for name-based upsert where available. Some carry a subset: classic-accounts is list-only, classic-computer-history and classic-mobile-history are get-only. classic-computer-app-usage (see above) carries its date range in the URL path and has no list/get pair.
Run jamf-cli commands -o table for the full list. Areas covered: computer and mobile device management, policies, configuration profiles, packages, patch management, network segments, printers, removable MAC addresses, administration.
# List all removable MAC address entries
jamf-cli pro classic-removable-mac-addresses list
# Get a specific entry by ID
jamf-cli pro classic-removable-mac-addresses get 1
# Get by name
jamf-cli pro classic-removable-mac-addresses get --name "Corporate Dock"
# Create or update by name
cat entry.xml | jamf-cli pro classic-removable-mac-addresses apply --yes
# Delete by name
jamf-cli pro classic-removable-mac-addresses delete --name "Old Dock" --yesclassic-ebooks exposes full CRUD plus scope management for eBook resources.
# List all eBooks
jamf-cli pro classic-ebooks list -o table
# Get an eBook by ID or name
jamf-cli pro classic-ebooks get 5
jamf-cli pro classic-ebooks get --name "iPad User Guide"
# Create or replace by name
cat ebook.xml | jamf-cli pro classic-ebooks apply --yes
# Delete by name
jamf-cli pro classic-ebooks delete --name "Outdated Guide" --yes
# Scope management: an id, or --name (see Scope Management)
jamf-cli pro classic-ebooks scope get 5
jamf-cli pro classic-ebooks scope get --name "iPad User Guide"
jamf-cli pro classic-ebooks scope add --name "iPad User Guide" --mobile-device-group "All iPads"
jamf-cli pro classic-ebooks scope remove 5 --section exclusion --building "London"classic-ebooks is the one resource whose scope holds both computers and mobile devices, and the only one carrying --class. See Scope Management for the category matrix and for why --class sends two requests.
classic-user-groups manages JSS user groups. These groups are referenced in policy and eBook scope limitations.
# List all JSS user groups
jamf-cli pro classic-user-groups list -o table
# Get a group by ID or name
jamf-cli pro classic-user-groups get 3
jamf-cli pro classic-user-groups get --name "Staff"
# Create or replace by name
cat user-group.xml | jamf-cli pro classic-user-groups apply --yes
# Delete by name
jamf-cli pro classic-user-groups delete --name "Old Group" --yesclassic-ldap-servers manages on-prem LDAP server connections (connection settings plus attribute mappings) via the Classic API, and is the only route to create, update or delete an LDAP server from the CLI. classic-ldaps get is a read-only mappings view.
# List all LDAP servers
jamf-cli pro classic-ldap-servers list -o table
# Get by ID or name
jamf-cli pro classic-ldap-servers get 3
jamf-cli pro classic-ldap-servers get --name "Corporate AD"
# Create or replace by name
cat ldap-server.xml | jamf-cli pro classic-ldap-servers apply --yes
# Delete by name
jamf-cli pro classic-ldap-servers delete --name "Decommissioned LDAP" --yesPrestage resources (computer-prestages, mobile-device-prestages) carry a versionLock field in their JSON body. On update, apply and the scope operations (create-scope, update-scope, delete-multiple), the CLI GETs the current resource and injects every versionLock it finds, nested ones under locationInformation, purchasingInformation and accountSettings included. A create through apply gets 0. You set no versionLock yourself.
pro computer-prestages create-scope 1sendsPOST /v2/computer-prestages/1/scopeandupdate-scopesends thePUT, each carrying the fetchedversionLock. On the mobile side the two bulk deletes arescope-delete-multiple(POST /v2/mobile-device-prestages/{id}/scope/delete-multiple) andattachments-delete-multiple(POST /v3/mobile-device-prestages/{id}/attachments/delete-multiple); a baredelete-multiplerefuses and names both replacements.
Export a prestage, edit it and pipe it back to update or apply: the CLI replaces the stale versionLock in your export with the current server value. On a concurrent modification between the CLI's GET and PUT, the server returns HTTP 409 Conflict.
Print a valid Jamf Pro access token, refreshing it if it has expired. Use it to debug API calls with curl, or to feed a token to another tool without running the OAuth2 flow yourself.
# Print token as JSON (includes expiry for oauth2/platform auth)
jamf-cli pro auth token
# Extract just the token string for scripting
jamf-cli pro auth token --field token
# Use in a curl command
curl -H "Authorization: Bearer $(jamf-cli pro auth token --field token)" \
https://jamf.company.com/api/v1/buildings
# Force a fresh token exchange (ignores cache)
jamf-cli pro auth token --refreshOutput:
{
"token": "eyJhbGciOi...",
"expires_at": "2026-04-15T15:30:00Z"
}With --auth-method token (pre-existing bearer token) the CLI omits expires_at: no expiry information is available.
| Flag | Description |
|---|---|
--refresh |
Force a new token exchange, ignoring any cached token |
On a platform gateway profile (auth-method: platform), 59 pro command entries are refused before a request is sent, with exit code 8 (Refused by policy). They sit outside the gateway's published Jamf Pro or Classic API. Several still answer today, on transitional routes.
List them from the binary in hand:
jamf-cli commands -o json \
| jq -r '.[] | select(.gateway=="unserved") | .command + "\t" + .gatewayBasis'A refused row carries gateway ("unserved"), gatewayBasis (unpublished for every current entry, meaning the published spec does not declare it) and gatewayDetail (the spec version that omits it, or the method it omits).
Verified on a live gateway profile (-p platform-mockingbird, EU, environment-scoped):
| Command | Result |
|---|---|
pro api-roles list |
exit 8, not part of the Jamf Platform gateway's published API
|
pro environment-type get |
exit 8, same |
pro mdm commands |
exit 8, detail "The gateway's Jamf Pro API 11.32.0 declares GET on this path but not POST." |
pro mobile-devices lock --serial <serial> --yes --confirm-destructive |
exit 8, same method-specific detail |
pro static-computer-groups list |
exit 0, returns the groups with a warning that the name retires on 2027-03-09 |
pro classic-patch-titles list |
exit 0, returns XML |
pro classic-patch-policies list |
exit 0, returns XML |
pro policy-properties get |
exit 0, returns JSON |
The refusal names the resolved credentials in its hint, so an env-var CI job gets the same answer as a profile:
$ jamf-cli -p platform-mockingbird pro api-roles list
hint: auth-method platform against the gateway, from profile "platform-mockingbird"
The command's own --help carries the caveat, so --help is enough to check before writing a script:
$ jamf-cli -p platform-mockingbird pro api-roles list --help
Get roles with Search Criteria
Through the Jamf Platform gateway: outside the published API and refused. Not declared by
the gateway's Jamf Pro API 11.32.0. The gateway may still route it today, but that is
transitional. Requires a profile pointed at a Jamf Pro instance (auth-method oauth2 or
token).
No refused command names a successor today, so no refusal or --help line carries a use this instead sentence, and no refused row in commands -o json carries a gatewaySuccessor key.
The families affected, all 59:
| Refused | Count | Notes |
|---|---|---|
pro mobile-devices MDM actions |
16 |
apply-redemption-code, clear-passcode, clear-restrictions-password, delete-user, disable-lost-mode, enable-lost-mode, lock, log-out-user, play-lost-mode-sound, refresh-cellular-plans, request-mirroring, restart, settings, shutdown, stop-mirroring, unlock-user-account. See Device Actions
|
pro api-integrations |
7 |
apply, client-credentials, create, delete, get, list, update
|
pro classic-computer-configs |
7 | The resource and its six verbs. Dead resource; the instance 404s it too |
pro api-authentication |
6 |
current, invalidate-token, keep-alive, list, oauth-token, token. The instance's own /v1/auth endpoints |
pro api-roles |
6 |
apply, create, delete, get, list, update. Use Jamf Pro's own UI or an instance profile |
pro computer-inventory MDM actions |
8 |
disable-remote-desktop, enable-remote-desktop, lock, restart, set-auto-admin-password, set-recovery-lock, settings, shutdown. erase, remove-mdm and flush-commands still work |
pro jamf-pro-initialization |
3 |
initialize, initialize-database-connection, platform-initialize. Bootstraps an on-prem Jamf Pro from an activation code; not applicable to a cloud tenant |
pro api-role-privileges |
2 |
list, search
|
pro environment-type get |
1 | |
pro macos-managed-software-updates list |
1 | |
pro mdm commands |
1 | The generic command-issuing endpoint |
pro sso-oauth-session-tokens list |
1 | The instance's own session-token endpoint |
pro auth token and platform auth token are hand-written and are not refused; use those to print a token.
Seven of the 59 are Classic (pro classic-computer-configs), whose gatewayDetail reads "not declared by the gateway's Classic API 11.32.0". The gateway's published Classic API is now at the same version as its Jamf Pro API, so the resource is absent at the current version rather than a stale one: it is dead, not pending. The instance 404s it too.
Keep the gateway profile for Platform API work and add an oauth2 profile pointed at the Jamf Pro instance for the refused commands:
jamf-cli -p my-instance pro computers lock --serial C02X1234 --yes --confirm-destructiveJAMF_CLI_ALLOW_UNPUBLISHED=1 downgrades an unpublished refusal to a stderr warning and sends the request. Neither --quiet nor --no-hints silences that warning, and the route it depends on is going away, so treat it as a stopgap. It has no effect on a probe refusal: there is no route to reach.
The reverse direction is refused the same way: a Platform-only command on an instance profile exits 8, naming the profile, its resolved auth method and platform setup.
Full detail, including the base URL change and the three scope levels, is in Platform API GA Migration.
A 403 names the permission it wanted in the vocabulary of the API that answered:
-
An instance request (
oauth2/tokenprofile) names Jamf Pro API-role privileges as Jamf Pro's own picker spells them:Read Categories. -
A gateway request names Jamf Account capability permissions plus the section and permission name Jamf Account's Platform API integration picker shows, so
categories:readrenders as Organizational context > Categories: Read.
Neither vocabulary converts to the other, so commands -o json carries both: privileges (Jamf Pro API-role names), gatewayPrivileges (capability slugs), gatewayPermissions (the picker's words) and api naming the serving API.
# Required permissions, and the serving API
jamf-cli commands -o json \
| jq '.[] | select(.command=="pro categories list")
| {api, privileges, gatewayPrivileges, gatewayPermissions}'doctor diagnoses local config, credentials and server reachability without auth, so it works when auth is broken.
# Diagnose the active profile
jamf-cli doctor
# Diagnose a specific profile by name
jamf-cli doctor my-profile
# Machine-readable output
jamf-cli doctor -o jsonOutput sections:
- CONFIG: path to the config file and whether it exists
-
ACTIVE PROFILE: name, URL, auth method, and credential references (secrets shown as
abcd••••fingerprints only). Flags any credential shadowed by an environment variable at runtime. - ENVIRONMENT: all credential-related env vars and their state
- CONNECTIVITY: unauthenticated HEAD probe of the profile URL with HTTP status and latency
The connectivity probe is a bare HEAD request testing DNS/TLS reachability.
jamf-cli config list --status # List profiles with health check
jamf-cli config show # Show resolved configuration
jamf-cli pro setup # Bootstrap OAuth2 credentials (see below)
jamf-cli platform setup # Configure platform gateway profile
jamf-cli completion install # Install shell completions
jamf-cli version # Print version info
jamf-cli version -v # Include spec provenance (file + SHA256 for each generated command source)
jamf-cli version -o json # Structured version reportversion reports the CLI version, build commit and date, and the Jamf Pro spec version the commands were generated against (specProVersion, 11.32.0 in v1.30.0). -v adds the source file and SHA256 hash per generated command group (pro, proClassic, platform), which helps when a command 404s against an older instance. -o json or -o yaml gives a machine-readable report.
pro setup writes OAuth2 client credentials for a Jamf Pro instance as a config profile, from one of two sources. You type each credential at an interactive prompt; no flag, environment variable or stdin route accepts one, so nothing reaches shell history, ps output or a CI log.
| Flag | Default | Description |
|---|---|---|
--credentials |
existing |
existing: you supply the client ID and secret for an API client you already created in Jamf Pro (Settings > API roles and clients); its own API role decides what the CLI can do. create: authenticates with a Jamf Pro account, creates an API role and client, and generates credentials. The username and password are never stored. |
--scope |
standard |
read-only, standard or full-admin. Scopes the role --credentials create builds. Refused with existing. |
--rotate-credentials |
false | Regenerate client credentials for an existing integration. --credentials create only. |
--profile-name |
default |
Profile name to write. Ignored with --from-file. |
--url |
Jamf Pro server URL | |
--from-file |
File of one Jamf Pro URL per line, for multi-instance setup. Profiles are auto-named pro-<subdomain>. |
With --credentials existing, the CLI exchanges the pair for a token before writing anything, so a mistyped secret fails at setup. With --from-file, existing asks for a client per instance: Jamf Pro issues an API client against the instance it lives on.
A flag that cannot apply to the chosen source is refused, exit 2, with nothing written:
$ jamf-cli pro setup --credentials existing --scope full-admin
--scope cannot be used with --credentials existing: the privileges come from the API role
already attached to your client in Jamf Pro
$ jamf-cli pro setup --credentials existing --rotate-credentials
--rotate-credentials cannot be used with --credentials existing: jamf-cli did not issue the
client and cannot rotate its secret; generate a new secret in Jamf Pro and re-run setup
$ jamf-cli pro setup --credentials bogus
invalid --credentials "bogus": must be one of: existing, create
--no-input is refused on both sources: both read their secrets from a prompt.
--credentials createdepends on an authentication path Jamf is retiring. Local Jamf Pro accounts, SAML and LDAP/directory administrator authentication are deprecated for cloud-hosted Jamf Pro, estimated removal in the second half of 2027; self-hosted instances are unaffected. Prefer an existing API client, orjamf-cli platform setup.pro setup --helpcarries the notice and the Jamf link.
Full walkthrough: Setup Guide.
On the first authenticated command against a profile, the CLI probes /api/v1/jamf-pro-version and compares it against the spec version the binary was built from. A tenant on an older Jamf Pro than the spec version earns a one-line warning on stderr:
warning: tenant is on Jamf Pro 11.20.0; this CLI was built against 11.32.0 — some commands may not be available
The CLI caches the result for 24 hours per profile (~/.config/jamf-cli/.version-cache.json), so the probe costs at most one extra request per profile per day. Commands still run. Skip it with --no-version-check or JAMF_NO_VERSION_CHECK=1, for CI/CD or tenants without the version endpoint.
This warns when the tenant is behind the CLI. For the CLI itself being behind its latest release, see Configuration & Profiles#Release Update Notice (v1.26.0+), which follows the same conventions: stderr only, 24-hour cache, three opt-outs, suppressed in CI.
Repository · Issues · Releases
jamf-cli Wiki
- Home
- Community
- Getting Started
- CLI Reference
- Product Commands
- Workflows
- Configuration
- Reference
Products
- Jamf Pro:
jamf-cli pro - Jamf Platform API:
jamf-cli pro(blueprints, benchmarks, DDM reports) - Jamf Platform:
jamf-cli platform(AI Governance, Jamf Account, audit) - Jamf Protect:
jamf-cli protect - Jamf School:
jamf-cli school - Jamf Security Cloud:
jamf-cli security