The Jamf Platform API reached general availability on 2026-09-03, and most of this release is that migration. If you use a auth-method: platform profile, read the migration guide before upgrading — it carries the detail, the error messages verbatim, and the reasoning behind each refusal.
If you only use oauth2 or token profiles against a Jamf Pro instance, the short version is: four small breaking changes (below), and Classic writes finally have --scaffold and --set.
Breaking — platform gateway profiles
-
The gateway base URL is now
https://{region}.api.jamfcloud.com. The pre-GAhttps://{region}.apigw.jamf.comis retired, and the/apipath segment it required is gone. A profile still naming the old host is refused by name before any request is sent — the CLI does not rewrite it for you, because the profile on disk stays wrong for every other tool reading it. The wire symptom without that guard is an edge-level 403 during the token exchange naming neither the host nor the reason. -
Public-beta credentials were revoked at GA. A beta client cannot be migrated — register a replacement API integration in Jamf Account and run
jamf-cli platform setup. -
Three scope levels, one per profile: organization, platform environment, tenant.
environment-idis new (--environment-id,JAMF_ENVIRONMENT_ID) and is the level to prefer;tenant-idis the legacy one; organization scope carries no ID at all and is selected by the gateway host. Supplying two levels together is refused, in the environment as well as in a profile. The scope now travels in anX-Environment-Id/X-Tenant-Idheader instead of a/tenant/{tenantId}URL segment. -
75 Jamf Pro and Classic commands are refused on a gateway profile, before a request is sent, with the new exit code 8 (
Refused by policy). They sit outside the gateway's published API. Several still answer today — that is transitional, and refusing now is cheaper than the eventual bare403 BAD_PERMISSIONSwith nothing saying a withdrawal caused it.The 24 that will bite hardest are device actions: all 16
pro mobile-devicesMDM subcommands (lock,clear-passcode,enable-lost-mode,restart,shutdown, …) and 8pro computers-inventoryones (lock,restart,set-recovery-lock,enable-remote-desktop, …). They POST/v2/mdm/commands, which the gateway routes but does not publish.pro comp eraseandpro comp remove-mdmare unaffected.The rest:
pro api-roles,pro api-integrations,pro api-roles-privileges,pro authentications,pro oauth-token-sessions,pro environment-type,pro database-connections,pro systems initialize/platform-initialize,pro mac-os-managed-software-updates list,pro mdm-commands commands,pro static-computer-groups(usepro computer-groups-static-groups),pro classic-computer-configs,pro classic-patch-reports, fivepro classic-patch-titlessubcommands,pro classic-patch-policies list, andpro policy-properties.The remedy is a second
oauth2profile against the instance. For the current list for the binary in hand:jamf-cli commands -o json | jq -r '.[] | select(.gateway=="unserved") | .command'JAMF_CLI_ALLOW_UNPUBLISHED=1downgrades an unpublished refusal to a stderr warning and sends the request anyway — a stopgap for one job, not a mode to settle into. The warning it substitutes cannot be silenced by--quietor--no-hints. -
The reverse direction is refused too, the same way: a Platform-only command on an instance profile exits 8 naming the profile, its resolved auth method and
platform setup, rather than reading as a credential problem. -
A platform command's 403 now exits 5, not 1. Platform commands previously returned the SDK's error untouched, so the one failure with a specific remedy exited with the generic code.
Breaking — everything else
pro ddm-reports declaration getandpro ddm-reports device getare removed. Both endpoints were deprecated upstream in favour of a sibling the CLI already shipped:declaration devices <id> --filter …anddevice declarations <id> --filter …. The successors declarefilterrequired, so there is no unfiltered read left — use--filter 'active=in=(true,false)'where you want everything.pro comp eraseandpro comp remove-mdmsend/v4/computers-inventory/{id}/…, up from/v1/computer-inventory/{id}/…. Neither has a version fallback, so an instance not serving v4 answers 404. Flags, targeting and confirmation are unchanged.jamf-cli config listno longer has atenant-idcolumn intable,csvandplainoutput; it hasenvironment-idanddefaultinstead.-o jsonand-o yamlare unchanged and still carrytenant-id. Parse the JSON, not the table.- Building from source needs Go 1.27 (
go.moddeclaresgo 1.27.0, up from1.26.6). The defaultGOTOOLCHAIN=autofetches it; a pinned older toolchain fails. Binary releases are unaffected.
Added
- Classic writes gained
--scaffoldand--set, plus required-field and enum lists in--help— 111 of 117create/update/applycommands, across 43 of the 54 Classic resources.--setbuilds the whole body and is mutually exclusive with--from-file; it refuses an unknown field, an out-of-enum value and a credential field, because the Classic API answers201and silently drops or defaults the first two. Classiccreateandupdatealso accept--from-file, not justapply. --fileaccepts YAML on generated Platform and Security Cloud commands, matching Pro's--from-file.- Jamf AI Governance —
platform ai-policies(aip),platform ai-tools(ait). - Jamf Account —
platform account-licenses,deal-registrations,distributor-configuration,distributor-purchase-orders,distributor-quotes,sso-connections,sso-domains. Organization scope, and US-only: a non-US profile is refused before sending. - Platform audit —
platform audit, environment scope only. Not to be confused withpro audit, which runs health checks against a Jamf Pro instance. - Jamf Security Cloud through the gateway —
security dns-*,ztna-*,content-categories,device-groups,uem-*,enrollment-activation-profiles. The two halves of Security Cloud take different credentials, so everysecuritycommand'sShortnow says which API serves it (platform gateway or Radar API), andcommands -o jsoncarries it asapi. - App Installers on the gateway. The endpoints are published upstream now, so the commands are generated from that spec rather than a reverse-engineered one, and are no longer refused on a gateway profile. New:
pro app-installers get,app-installer-titles versions,app-installer-global-settings deployment-controls/history/add-history-note. - Jamf Protect
backupandrestore, capturing and replaying a whole tenant in the portable form eachexportproduces, resolving every cross-resource reference by name. Plusprotect analytics overridesfor the tenant overlay (tenantSeverity/tenantActions) on Jamf-managed analytics, whichanalytics exportsilently dropped. pro ddm-reports declaration devicesanddevice declarationsgained--page; only the first page was reachable before.
Changed
- An empty list prints
[], notnull— Pro'slist --alland every Platform and Security Cloud list.jqpipelines previously failed with "Cannot iterate over null" on exactly the tenants where a collection was empty. pro computers-inventorysends/v4instead of/v3, andgetreads the v4 detail endpoint. The two v4-only operations (erase,remove-mdm-profile) exist at all for the first time. Generated subcommands retry the/v1path on a 404 and warn on stderr.- A 403 names the permission in the vocabulary of the API that answered it — capability permissions with Jamf Account's own section and permission names for a gateway request, Jamf Pro API-role privilege names for an instance request. The two do not convert to each other, so printing the wrong one sent you to a console where the grant it named does not exist.
commands -o jsoncarries both (privileges,gatewayPrivileges,gatewayPermissions). - A CDN/WAF refusal is reported as one, rather than as
permission denied (HTTP 403)with an HTML page in the message and a hint about API roles. Known triggers:file://anywhere in a request body,.pkgupload content, a burst of writes. A.pkgupload through a gateway profile is currently refused — upload through an instance profile. -n, --dry-runis honoured on Platform and Security Cloud writes, printing method, resolved path and body to stderr, and now prints before the confirmation prompt rather than after it. Hand-written platform writes with no per-command preview are refused under-nrather than executed.-vlabels retried requests with the attempt number and the wait, so a slow call is distinguishable from a retry sequence.
Fixed
- A YAML request body carrying a timestamp scalar or a non-string mapping key — both legal YAML, neither expressible in JSON — was reported as malformed input.
pro platform-device-groupsname lookups built a stale/tenant/{id}/path, which collapsed to/tenant//under environment or organization scope.JAMF_ENVIRONMENT_IDand--tenant-idnow override a profile's scope, the way every other credential input here does. Previously only the profile was read on some paths, so a documented override silently acted against the wrong tenant.pro diffread one directory level and hid every nested resource; it also mis-keyed every compliance benchmark.--scaffoldshows what an array holds instead of an empty[], and enum-constrained fields — including array elements — name their allowed values in--help.
Full changelog: v1.27.0...v1.28.0