Orchestrating the Status Channel, Part 2: Configuring Jamf Pro Webhooks for DDM Events

SCCM status filter rules let you react to management events rather than poll for them. When a specific condition occurs, such as a policy run failing or a client reporting a non-compliant baseline, a rule fires and an action executes. Intune compliance webhook notifications work on the same principle. The management platform pushes the event to an external endpoint; the endpoint decides what to do.

Jamf Pro webhooks are the equivalent mechanism. When a defined event occurs in your Jamf Pro environment, Jamf Pro sends an HTTP POST to a configured URL. That POST carries a JSON payload describing the event and the device that triggered it. Your receiving endpoint reads the payload and acts. The architectural shift is the same one DDM itself represents: push over pull.

Capabilities and Limitations

Before building a webhook-driven workflow, it is important to be clear about what Jamf Pro webhooks can and cannot do in a DDM context.

The easiest mistake to make when designing a DDM notification workflow is assuming the webhook can tell you whether a declaration is non-compliant. It cannot. Jamf Pro webhooks fire on management events: device check-ins, inventory updates completing, and device additions. There is no webhook event type tied to declaration evaluation outcomes. A webhook does not know that a device’s passcode configuration just evaluated to invalid. It knows the device communicated with Jamf Pro.

That distinction changes the design. You cannot configure a webhook that says “tell me when a declaration becomes non-compliant.” You configure a webhook that says “tell me when a device checks in,” and then query the declaration status from the API at that point.

The webhook is the signal that a device has interacted with Jamf Pro. The status query is the verification of what state the device is actually in.

The event types most useful for DDM-adjacent workflows are:

  • ComputerCheckIn: Fires when a macOS computer checks in with Jamf Pro. The most common trigger for DDM status verification, because check-in often coincides with declaration processing.
  • MobileDeviceCheckIn: The equivalent event for iOS and iPadOS devices.
  • ComputerInventoryCompleted: Fires after a macOS computer submits a full inventory update, indicating a more complete communication with Jamf Pro than a simple check-in.

The webhook misconception versus reality: the webhook fires on device check-in, not on a declaration outcome, and compliance is determined afterward through a separate status query.

Webhook Object Structure

Webhooks are a Classic API resource. There is no webhook endpoint in the modern Jamf Pro API. You create one by sending an XML body to POST /JSSResource/webhooks/id/0, where the 0 tells Jamf Pro to assign a new ID (the create-by-ID convention the Classic API uses across resources).

<webhook>
  <name>DDM Status Check Trigger</name>
  <enabled>true</enabled>
  <url>https://your-webhook-receiver.admincrossover.dev/api/jamf-status</url>
  <content_type>application/json</content_type>
  <event>ComputerCheckIn</event>
  <authentication_type>HEADER</authentication_type>
  <header_name>Authorization</header_name>
  <header_value>your-pre-shared-secret-token</header_value>
</webhook>

Each field serves a specific purpose:

  • name: A human-readable identifier shown in the Jamf Pro UI and API responses. Use something that indicates which workflow the webhook supports.
  • enabled: Controls whether the webhook is active. Set to false to pause without deleting.
  • url: The full URL of the endpoint that will receive the HTTP POST. Must be reachable from your Jamf Pro instance over HTTPS. Jamf Pro webhooks require a valid trusted certificate; self-signed certificates are not accepted.
  • content_type: The format of the POST body Jamf Pro sends to your receiver, either application/json or application/xml.
  • event: The single Jamf Pro event that triggers this webhook. One webhook handles one event.
  • authentication_type: How Jamf Pro authenticates to your receiver: NONE, BASIC, or HEADER. For a pre-shared secret, use HEADER with header_name and header_value — Jamf Pro sends that header on every POST and your endpoint checks it. For BASIC, supply <username> and <password> instead.
  • connection_timeout / read_timeout: Optional integers (seconds) for how long Jamf Pro waits when delivering the webhook.

Incoming POST Body

When a ComputerCheckIn event fires, Jamf Pro sends a POST to the configured url. With content_type set to application/json, the body looks like this (the XML form carries the same structure inside a <JSSEvent> root):

{
  "webhook": {
    "id": 1,
    "name": "DDM Status Check Trigger",
    "webhookEvent": "ComputerCheckIn",
    "eventTimestamp": 1787023800972
  },
  "event": {
    "trigger": "CLIENT_CHECKIN",
    "username": "jappleseed",
    "computer": {
      "udid": "33333333-3333-3333-3333-333333333333",
      "deviceName": "admincrossover-macbook-pro",
      "model": "MacBook Pro (16-inch, 2021)",
      "serialNumber": "C02ABCDEFGHIJK",
      "osVersion": "15.5.0",
      "ipAddress": "203.0.113.10",
      "reportedIpV4Address": "10.0.1.100",
      "jssID": 42,
      "managementId": "22222222-2222-2222-2222-222222222222"
    }
  }
}

Two structural points that matter for the code in Part 3:

  • The top level has a webhook object (webhook metadata, with the event name in webhookEvent) and an event object. The event object carries a trigger and the acting username; the device details are nested one level deeper, in event.computer.
  • The identifiers your downstream script needs live in event.computer, not directly on event. event.computer.managementId is the clientManagementId used in DDM API calls (Part 1); event.computer.jssID is the numeric Jamf Pro computer ID, a fallback when managementId is absent.

Creating and Auditing Webhooks with PowerShell

The examples below use AdminCrossover.Tools for authentication, so session and header management stay out of the way of the webhook-specific logic.

Creating a Webhook

# Authenticate to Jamf Pro and store the session for subsequent calls.
$Session = Connect-ACJamfPro `
    -JamfUrl      'https://yourinstance.jamfcloud.com' `
    -ClientId     $env:JAMF_CLIENT_ID `
    -ClientSecret $env:JAMF_CLIENT_SECRET

# Define the webhook as a Classic API XML body. Authentication to the receiver is
# HEADER-based: Jamf Pro sends header_name/header_value on every outgoing POST.
$WebhookXml = @"
<webhook>
    <name>DDM Status Check Trigger</name>
    <enabled>true</enabled>
    <url>https://your-receiver.admincrossover.dev/api/jamf-status</url>
    <content_type>application/json</content_type>
    <event>ComputerCheckIn</event>
    <authentication_type>HEADER</authentication_type>
    <header_name>Authorization</header_name>
    <header_value>$($env:JAMF_WEBHOOK_TOKEN)</header_value>
</webhook>
"@

# Create the webhook. Webhooks are a Classic API resource, so the endpoint is the full
# /JSSResource path and the body is XML. Invoke-ACJamfApi sends string bodies as-is, so
# set -ContentType to application/xml. Id 0 tells Jamf Pro to create a new webhook.
$Result = Invoke-ACJamfApi `
    -Endpoint    '/JSSResource/webhooks/id/0' `
    -Method      Post `
    -Body        $WebhookXml `
    -ContentType 'application/xml' `
    -Accept      'application/xml'

Write-Host 'Webhook created.'

Auditing Existing Webhooks

Reviewing the current webhook configuration is the equivalent of auditing SCCM status filter rules or listing Intune notification policies.

# Retrieve all configured webhooks. Classic API reads can return JSON via the Accept
# header, which keeps the output easy to work with in PowerShell.
$WebhooksResponse = Invoke-ACJamfApi `
    -Endpoint '/JSSResource/webhooks' `
    -Method   Get `
    -Accept   'application/json'

# Surface the fields that matter for an audit.
$WebhooksResponse.Data.webhooks |
    Select-Object -Property name, url, event, enabled |
    Format-Table -AutoSize

Each webhook object in .Data has the same fields that were submitted on creation. A webhook with enabled: false is configured but not firing. If you see duplicate event types pointing to the same URL, that is a signal that webhook creation ran more than once without a prior audit. Each Jamf Pro webhook handles exactly one event type; multiple event types require multiple webhook objects.

What the Receiving Endpoint Must Implement

Part 3 of this series builds the PowerShell logic that processes incoming webhook payloads. Before that, it is worth stating what any webhook consumer needs to handle at a minimum, so the contract between Jamf Pro and the receiver is clear.

The receiving endpoint must accept HTTP POST requests on a publicly accessible HTTPS URL with a valid trusted certificate. On each incoming request, it must read the Authorization header and compare it against the pre-shared token configured in Jamf Pro. If the values do not match, the request should be rejected. A mismatch means the POST did not come from your Jamf Pro instance.

Once validated, the endpoint reads the JSON body, extracts the device identifiers from the event.computer object, and passes them to the downstream processing logic. For DDM workflows, the relevant identifiers are event.computer.managementId for the DDM API calls and event.computer.jssID as a fallback for inventory lookup when managementId is absent.

The endpoint does not determine DDM compliance. It receives the event, validates it, and routes the identifiers. The compliance determination comes from the status query in Part 3.

In small environments, processing each webhook synchronously (validating the token, querying status, and acting on the result within the same request) is workable. In larger environments, high check-in frequency means webhooks can arrive faster than a synchronous handler can process them. The safer architecture queues incoming events and processes them asynchronously, so a burst of check-ins after a software update release does not overwhelm the automation host. Queuing also provides a natural retry surface: if the Jamf Pro API is temporarily unavailable, the event remains in the queue rather than being dropped.

Sequence of a webhook request: Jamf Pro POSTs the check-in event, the receiver validates the token and rejects a mismatch, extracts managementId, then routes to the DDM status query continued in Part 3.

Failure Modes

Webhook delivery issues are usually silent from Jamf Pro’s perspective: if the endpoint does not respond, Jamf Pro does not retry indefinitely. Understanding where each failure surfaces helps you build appropriate defensive handling.

Webhook POST is not received. The most common causes are a misconfigured url, a firewall or network policy blocking inbound traffic to the receiver, or an expired TLS certificate on the receiving endpoint. Jamf Pro webhooks require a valid trusted certificate; a self-signed or expired certificate will cause delivery to fail silently from the Jamf side. Verify reachability from the Jamf Pro host using the built-in test option in the webhook configuration UI.

Authorization header mismatch. The receiver gets the POST but rejects it because the Authorization header does not match the configured pre-shared token. This happens most often after rotating the token in Jamf Pro without updating the receiver’s expected value. Treat this as a configuration drift problem: the webhook and the receiver must always reference the same token value.

Duplicate ComputerCheckIn events. A single device checking in may generate more than one webhook event, particularly when multiple webhook objects are configured for the same event type. The receiver should handle the same managementId appearing in rapid succession without triggering multiple redundant API calls or sync commands. Design the downstream logic to be idempotent. Calling the status query twice for the same device in quick succession produces the same result, so the cost is an extra API call rather than a correctness problem.

Webhook fires but no declaration status exists. The device checked in but has not yet sent a DDM status report. The downstream status query returns an empty result. This is not a webhook failure; it is a timing issue. Logging the event and exiting gracefully is the right behavior. The next check-in will either carry a status report or trigger another webhook.

event not matching expectations. Each Jamf Pro webhook handles exactly one event. If the receiver is configured to handle ComputerCheckIn but the webhook object was accidentally created for ComputerInventoryCompleted, the receiver will never see check-in events. Audit the configured webhooks using GET /JSSResource/webhooks to confirm the event matches what the receiver expects.

The operating rule

Jamf Pro webhooks fire on management events, not on DDM declaration outcomes. Use them to signal that a device has interacted with Jamf Pro, then perform a status query to determine the actual declaration state. The webhook is the trigger. The API query is the verification. Do not conflate the two or skip the verification step.