
For this post, I wanted to share how to get important notifications that may be on your Jamf Pro server without having to actually open a web browser, navigating to your Jamf Pro server, logging in, and viewing the notifications.
This task can be done utilizing the Jamf Pro API. The endpoint we will be focusing on is /api/v1/notifications
Why is this important? Jamf Pro will notify if there are issues that need to be addressed. These notifications can include things like the ADE token expiring soon, the APNS certificate is expired, or errors with the connected Jamf Infrastructure Manager. There is a need to get alerted on these notifications so the issues can be addressed before they become part of a much larger issue.
Let’s dig in…
When using the Jamf Pro API, a valid Jamf Pro account is needed. When creating Jamf Pro User Accounts for the purpose of using with the API, refer to the following link on what permissions are needed for each specific task:
https://developer.jamf.com/jamf-pro/docs/privileges-and-deprecations
For the endpoint /v1/notifications with the get operation, there are no privileges needed. This service can be created with no privileges and it will still work for this purpose.


Now that the account is created in Jamf Pro, we can start building the script that we will use.
When using the Jamf Pro API, we need to use an account to authenticate, we will use the account created in the previous step and we will look at how to generate a bearer token with the API. Supply the correct username, password, and Jamf Pro URL and our script will use those variables to generate a bearer token that will be used to authorize our API calls.
#!/bin/bash
# API Credentials
jamfUser="api_Notifications"
jamfPass="jamf1234"
jssURL="https://techitout.jamfcloud.com"
# Encode Credentials
encodedCredentials=$( printf "${jamfUser}:${jamfPass}" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )
# Generate an auth token
authToken=$( /usr/bin/curl "${jssURL}/uapi/auth/token" \
--silent \
--request POST \
--header "Authorization: Basic ${encodedCredentials}"
)
# Parse authToken for token, omit expiration
token=$( /usr/bin/awk -F \" '{ print $4 }' <<< "${authToken}" | /usr/bin/xargs )
If we then do an echo $token we can see our Bearer Token value:
username@computername ~ % echo $token
eyJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkLWFwcCI6IkdFTkVSSUMiLCJhdXRoZW50aWNhdGlvbi10eXBlIjoiSlNTIiwiZ3JvdXBzIjpbXSwic3ViamVjdC10eXBlIjoiSlNTX1VTRVJfSUQiLCJ0b2tlbi11dWlkIjoiY2JhMDU5ZDYtZGNiNC00ZDg2LTg5MjYtMTc0NGZkOGY4YzI0IiwibGRhcC1zZXJ2ZXItaWQiOi0xLCJzdWIiOiIxMyIsImV4cCI6MTY0ODY4MjkzN30.FF5s6LcNJue-tkCcMSf7zlOWRTf-Kv30K-eGwVVddF0
Now that we have the authentication portion of our script taken care of, we can now build the rest of our script. There are several possible notifications that Jamf Pro can give us, we can take these values and store them in an array of possibilities like this:
# Build an array of possible important notifications from Jamf Pro
noticationsArr=(
APNS_CERT_REVOKED
APNS_CONNECTION_FAILURE
APPLE_SCHOOL_MANAGER_T_C_NOT_SIGNED
BUILT_IN_CA_EXPIRED
BUILT_IN_CA_EXPIRING
BUILT_IN_CA_RENEWAL_FAILED
BUILT_IN_CA_RENEWAL_SUCCESS
CLOUD_LDAP_CERT_EXPIRED
CLOUD_LDAP_CERT_WILL_EXPIRE
COMPUTER_SECURITY_SSL_DISABLED
DEP_INSTANCE_EXPIRED
DEP_INSTANCE_WILL_EXPIRE
DEVICE_ENROLLMENT_PROGRAM_T_C_NOT_SIGNED
EXCEEDED_LICENSE_COUNT
FREQUENT_INVENTORY_COLLECTION_POLICY
GSX_CERT_EXPIRED
GSX_CERT_WILL_EXPIRE
HCL_BIND_ERROR
HCL_ERROR
INSECURE_LDAP
INVALID_REFERENCES_EXT_ATTR
INVALID_REFERENCES_POLICIES
INVALID_REFERENCES_SCRIPTS
JAMF_CONNECT_UPDATE
JAMF_PROTECT_UPDATE
JIM_ERROR
LDAP_CONNECTION_CHECK_THROUGH_JIM_FAILED
LDAP_CONNECTION_CHECK_THROUGH_JIM_SUCCESSFUL
MDM_EXTERNAL_SIGNING_CERTIFICATE_EXPIRED
MDM_EXTERNAL_SIGNING_CERTIFICATE_EXPIRING
MDM_EXTERNAL_SIGNING_CERTIFICATE_EXPIRING_TODAY
MII_HEARTBEAT_FAILED_NOTIFICATION
MII_INVENTORY_UPLOAD_FAILED_NOTIFICATION
MII_UNATHORIZED_RESPONSE_NOTIFICATION
PATCH_EXTENTION_ATTRIBUTE
PATCH_UPDATE
POLICY_MANAGEMENT_ACCOUNT_PAYLOAD_SECURITY_MULTIPLE
POLICY_MANAGEMENT_ACCOUNT_PAYLOAD_SECURITY_SINGLE
PUSH_CERT_EXPIRED
PUSH_CERT_WILL_EXPIRE
PUSH_PROXY_CERT_EXPIRED
SSO_CERT_EXPIRED
SSO_CERT_WILL_EXPIRE
TOMCAT_SSL_CERT_EXPIRED
TOMCAT_SSL_CERT_WILL_EXPIRE
USER_INITIATED_ENROLLMENT_MANAGEMENT_ACCOUNT_SECURITY_ISSUE
USER_MAID_DUPLICATE_ERROR
USER_MAID_MISMATCH_ERROR
USER_MAID_ROSTER_DUPLICATE_ERROR
VPP_ACCOUNT_EXPIRED
VPP_ACCOUNT_WILL_EXPIRE
VPP_TOKEN_REVOKED
DEVICE_COMPLIANCE_CONNECTION_ERROR
CONDITIONAL_ACCESS_CONNECTION_ERROR
AZURE_AD_MIGRATION_REPORT_GENERATED
)
We now know what the possible notifications are, so let’s get the notifications from our Jamf Pro server. Notice this is where we use our Bearer token that was generated earlier:
# Returns important notifications from Jamf Pro in JSON
data=$( curl --request GET \
--url "${jssURL}/api/v1/notifications" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${token}" \
)
The next step we need to do is set up a loop to find our notifications match a notification in the array that we set earlier. The logic for this loop will look like:
for str in ${notificationsArr[@]}; do
if [[ " ${data} " =~ ${str} ]]; then
# Notification in list matches notification from $data
fi
done
Now, I’m sure that we want to do more with this data than just compare to a list. The next step is to populate our loop with useful steps that will give us the information that we want to know in a readable form.
# Loop each notification possibility and if it exists in the results, grab information on the notification using jq and notify
for str in ${noticationsArr[@]}; do
if [[ " ${data} " =~ ${str} ]]; then
# Create a clean string for read ability
cleanString=$( echo $str | sed 's/_/ /g' )
name=$( echo ${data} | jq --arg v "${str}" '.[]|select(.type==$v).params.name')
# Create a clean name for read ability
cleanname=$( echo $name | sed 's/"//g')
days=$( echo ${data} | jq --arg v "${str}" '.[]|select(.type==$v).params.days')
id=$( echo ${data} | jq --arg v "${str}" '.[]|select(.type==$v).params.id')
if [[ ${name} != "null" ]]; then
if [[ ${days} != "null" ]]; then
if [[ ${id} != "null" ]]; then
message=$( echo "$JPInstance Notification: $cleanString for $cleanname in $days days" )
else
message=$( echo "$JPInstance Notification: $cleanString for $cleanname in $days days" )
fi
else
message=$( echo "$JPInstance Notification: $cleanString for $name" )
fi
else
message=$( echo "$JPInstance Notification: $cleanString" )
fi
# Echo the message for each alert
echo ${message}
fi
done
Putting it all together:
#!/bin/bash
# API Credentials
jamfUser="api_Notifications"
jamfPass="jamf1234"
jssURL="https://techitout.jamfcloud.com"
# Encode Credentials
encodedCredentials=$( printf "${jamfUser}:${jamfPass}" | /usr/bin/iconv -t ISO-8859-1 | /usr/bin/base64 -i - )
# Generate an auth token
authToken=$( /usr/bin/curl "${jssURL}/uapi/auth/token" \
--silent \
--request POST \
--header "Authorization: Basic ${encodedCredentials}"
)
# Parse authToken for token, omit expiration
token=$( /usr/bin/awk -F \" '{ print $4 }' <<< "${authToken}" | /usr/bin/xargs )
# Build an array of possible important notifications from Jamf Pro
noticationsArr=(
APNS_CERT_REVOKED
APNS_CONNECTION_FAILURE
APPLE_SCHOOL_MANAGER_T_C_NOT_SIGNED
BUILT_IN_CA_EXPIRED
BUILT_IN_CA_EXPIRING
BUILT_IN_CA_RENEWAL_FAILED
BUILT_IN_CA_RENEWAL_SUCCESS
CLOUD_LDAP_CERT_EXPIRED
CLOUD_LDAP_CERT_WILL_EXPIRE
COMPUTER_SECURITY_SSL_DISABLED
DEP_INSTANCE_EXPIRED
DEP_INSTANCE_WILL_EXPIRE
DEVICE_ENROLLMENT_PROGRAM_T_C_NOT_SIGNED
EXCEEDED_LICENSE_COUNT
FREQUENT_INVENTORY_COLLECTION_POLICY
GSX_CERT_EXPIRED
GSX_CERT_WILL_EXPIRE
HCL_BIND_ERROR
HCL_ERROR
INSECURE_LDAP
INVALID_REFERENCES_EXT_ATTR
INVALID_REFERENCES_POLICIES
INVALID_REFERENCES_SCRIPTS
JAMF_CONNECT_UPDATE
JAMF_PROTECT_UPDATE
JIM_ERROR
LDAP_CONNECTION_CHECK_THROUGH_JIM_FAILED
LDAP_CONNECTION_CHECK_THROUGH_JIM_SUCCESSFUL
MDM_EXTERNAL_SIGNING_CERTIFICATE_EXPIRED
MDM_EXTERNAL_SIGNING_CERTIFICATE_EXPIRING
MDM_EXTERNAL_SIGNING_CERTIFICATE_EXPIRING_TODAY
MII_HEARTBEAT_FAILED_NOTIFICATION
MII_INVENTORY_UPLOAD_FAILED_NOTIFICATION
MII_UNATHORIZED_RESPONSE_NOTIFICATION
PATCH_EXTENTION_ATTRIBUTE
PATCH_UPDATE
POLICY_MANAGEMENT_ACCOUNT_PAYLOAD_SECURITY_MULTIPLE
POLICY_MANAGEMENT_ACCOUNT_PAYLOAD_SECURITY_SINGLE
PUSH_CERT_EXPIRED
PUSH_CERT_WILL_EXPIRE
PUSH_PROXY_CERT_EXPIRED
SSO_CERT_EXPIRED
SSO_CERT_WILL_EXPIRE
TOMCAT_SSL_CERT_EXPIRED
TOMCAT_SSL_CERT_WILL_EXPIRE
USER_INITIATED_ENROLLMENT_MANAGEMENT_ACCOUNT_SECURITY_ISSUE
USER_MAID_DUPLICATE_ERROR
USER_MAID_MISMATCH_ERROR
USER_MAID_ROSTER_DUPLICATE_ERROR
VPP_ACCOUNT_EXPIRED
VPP_ACCOUNT_WILL_EXPIRE
VPP_TOKEN_REVOKED
DEVICE_COMPLIANCE_CONNECTION_ERROR
CONDITIONAL_ACCESS_CONNECTION_ERROR
AZURE_AD_MIGRATION_REPORT_GENERATED
)
# Returns important notifications from Jamf Pro in JSON
data=$( curl --request GET \
--url "${jssURL}/api/v1/notifications" \
--header "Accept: application/json" \
--header "Authorization: Bearer ${token}" \
)
# Loop each notification possibility and if it
# exists in the results, grab information on the
# notification using jq and notify
for str in ${noticationsArr[@]}; do
if [[ " ${data} " =~ ${str} ]]; then
cleanString=$( echo $str | sed 's/_/ /g' )
name=$( echo ${data} | jq --arg v "${str}" '.[]|select(.type==$v).params.name')
cleanname=$( echo $name | sed 's/"//g')
days=$( echo ${data} | jq --arg v "${str}" '.[]|select(.type==$v).params.days')
id=$( echo ${data} | jq --arg v "${str}" '.[]|select(.type==$v).params.id')
if [[ ${name} != "null" ]]; then
if [[ ${days} != "null" ]]; then
if [[ ${id} != "null" ]]; then
message=$( echo "$JPInstance Notification: $cleanString for $cleanname in $days days" )
else
message=$( echo "$JPInstance Notification: $cleanString for $cleanname in $days days" )
fi
else
message=$( echo "$JPInstance Notification: $cleanString for $name" )
fi
else
message=$( echo "$JPInstance Notification: $cleanString" )
fi
echo $message
fi
done
exit 0
This will yield results similar to:
TECHITOUT Notification: DEP INSTANCE WILL EXPIRE for TIO ABM in 17 days
TECHITOUT Notification: USER INITIATED ENROLLMENT MANAGEMENT ACCOUNT SECURITY ISSUE
And thats it. I’ve also put it up on my GitHub: https://github.com/robjschroeder/Jamf-API-Scripts/blob/main/api-GetJPNotifications.sh
Thanks for checking it out!