Jamf Protect API — Example Calls

Practical, copy-paste GraphQL recipes for common Jamf Protect tasks.

This page shows what you can do with the Jamf Protect API through practical, copy-paste examples. Every query and mutation below is valid against the current schema.

All requests are a single POST to the Protect GraphQL endpoint through the Platform API Gateway, with a Bearer token in the Authorization header and your tenant ID in X-Tenant-Id. See the overview for how to create an integration and obtain a token.

{region} is us, eu, or apac, matching the region your Protect tenant is hosted in.

How to send a query

Wrap any GraphQL query in a JSON body under the query key and POST it:

curl --header "Content-Type: application/json" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --request POST \
  --url "https://{region}.api.jamfcloud.com/protect" \
  --data '{"query": "query { listComputers(input: { pageSize: 5 }) { items { hostName uuid } } }"}'

Each operation also requires a capability permission on your integration. For example, listComputers requires devices:read. The overview has the full operation-to-permission table.

Each example below comes in two parts:

  • A GraphQL block, which is the query or mutation itself. This is the actual operation in GraphQL's query language: it describes exactly the data you're asking for (and the fields you want back). Paste it into a GraphQL client such as Altair or Apollo Sandbox, or into the query field of the request above.
  • A cURL / Python block, with runnable examples that take that same query and send it to the endpoint over HTTP. Switch tabs to see each language.

So: the GraphQL block is what you're requesting; the cURL and Python tabs are how to send it.

Reading data

List enrolled computers

Return a page of computers with basic hardware and OS details.

query ListComputers {
  listComputers(input: { pageSize: 25 }) {
    items {
      uuid
      hostName
      serial
      osString
      modelName
      checkin
      connectionStatus
    }
    pageInfo {
      next
      total
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"query ListComputers { listComputers(input: { pageSize: 25 }) { items { uuid hostName serial osString modelName checkin connectionStatus } pageInfo { next total } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
query ListComputers {
  listComputers(input: { pageSize: 25 }) {
    items {
      uuid
      hostName
      serial
      osString
      modelName
      checkin
      connectionStatus
    }
    pageInfo {
      next
      total
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Paginate through results

Every list returns a pageInfo.next cursor. Pass it back as next to fetch the following page; when next is null, you've reached the end.

query NextPageOfComputers {
  listComputers(input: { pageSize: 50, next: "PASTE_PAGEINFO_NEXT_CURSOR" }) {
    items {
      uuid
      hostName
    }
    pageInfo {
      next
      total
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"query NextPageOfComputers { listComputers(input: { pageSize: 50, next: \"PASTE_PAGEINFO_NEXT_CURSOR\" }) { items { uuid hostName } pageInfo { next total } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
query NextPageOfComputers {
  listComputers(input: { pageSize: 50, next: "PASTE_PAGEINFO_NEXT_CURSOR" }) {
    items {
      uuid
      hostName
    }
    pageInfo {
      next
      total
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Find high-severity, unresolved alerts

Filter and sort server-side. This returns new (unresolved) High-severity alerts, newest first, with the affected computer inlined.

query HighSeverityOpenAlerts {
  listAlerts(input: {
    pageSize: 25
    filter: {
      severity: { equals: High }
      status: { equals: "New" }
    }
    order: { field: created, direction: DESC }
  }) {
    items {
      uuid
      created
      severity
      status
      eventType
      computer {
        hostName
        serial
      }
    }
    pageInfo {
      next
      total
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"query HighSeverityOpenAlerts { listAlerts(input: { pageSize: 25 filter: { severity: { equals: High } status: { equals: \"New\" } } order: { field: created, direction: DESC } }) { items { uuid created severity status eventType computer { hostName serial } } pageInfo { next total } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
query HighSeverityOpenAlerts {
  listAlerts(input: {
    pageSize: 25
    filter: {
      severity: { equals: High }
      status: { equals: "New" }
    }
    order: { field: created, direction: DESC }
  }) {
    items {
      uuid
      created
      severity
      status
      eventType
      computer {
        hostName
        serial
      }
    }
    pageInfo {
      next
      total
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Filters compose with and / or / not, and support operators like equals, contains, in, greaterThan, and beforeInterval / afterInterval for dates. For example, alerts from the last 24 hours on a specific plan:

query RecentAlertsForPlan {
  listAlerts(input: {
    pageSize: 50
    filter: {
      and: [
        { created: { afterInterval: "PT24H" } },
        { plan: { equals: "PLAN-ID" } }
      ]
    }
  }) {
    items {
      uuid
      created
      severity
      eventType
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"query RecentAlertsForPlan { listAlerts(input: { pageSize: 50 filter: { and: [ { created: { afterInterval: \"PT24H\" } }, { plan: { equals: \"PLAN-ID\" } } ] } }) { items { uuid created severity eventType } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
query RecentAlertsForPlan {
  listAlerts(input: {
    pageSize: 50
    filter: {
      and: [
        { created: { afterInterval: "PT24H" } },
        { plan: { equals: "PLAN-ID" } }
      ]
    }
  }) {
    items {
      uuid
      created
      severity
      eventType
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Get a single alert with full detail

Look up one alert by UUID, including the raw event payload (json).

query GetAlert {
  getAlert(uuid: "6f250316-2cfb-4521-8cb7-bfaf46497bc5") {
    uuid
    severity
    status
    eventType
    created
    tags
    computer {
      hostName
      serial
      osString
    }
    json
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"query GetAlert { getAlert(uuid: \"6f250316-2cfb-4521-8cb7-bfaf46497bc5\") { uuid severity status eventType created tags computer { hostName serial osString } json } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
query GetAlert {
  getAlert(uuid: "6f250316-2cfb-4521-8cb7-bfaf46497bc5") {
    uuid
    severity
    status
    eventType
    created
    tags
    computer {
      hostName
      serial
      osString
    }
    json
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Pull a compliance / insights scorecard

Each computer carries insight (compliance) pass/fail counts and posture fields, which are useful for a fleet compliance overview.

query ComplianceOverview {
  listComputers(input: { pageSize: 100 }) {
    items {
      hostName
      serial
      insightsStatsPass
      insightsStatsFail
      insightsStatsUnknown
      fullDiskAccess
      webProtectionActive
      insightsUpdated
    }
    pageInfo {
      total
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"query ComplianceOverview { listComputers(input: { pageSize: 100 }) { items { hostName serial insightsStatsPass insightsStatsFail insightsStatsUnknown fullDiskAccess webProtectionActive insightsUpdated } pageInfo { total } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
query ComplianceOverview {
  listComputers(input: { pageSize: 100 }) {
    items {
      hostName
      serial
      insightsStatsPass
      insightsStatsFail
      insightsStatsUnknown
      fullDiskAccess
      webProtectionActive
      insightsUpdated
    }
    pageInfo {
      total
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Browse the analytics (detection) catalog

List the analytics available in your tenant, including severity and category.

query DetectionCatalog {
  listAnalytics {
    items {
      uuid
      name
      severity
      categories
      description
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"query DetectionCatalog { listAnalytics { items { uuid name severity categories description } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
query DetectionCatalog {
  listAnalytics {
    items {
      uuid
      name
      severity
      categories
      description
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Inspect a plan and its analytics

query PlanDetail {
  getPlan(id: "1") {
    id
    name
    description
    logLevel
    analytics {
      name
      severity
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"query PlanDetail { getPlan(id: \"1\") { id name description logLevel analytics { name severity } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
query PlanDetail {
  getPlan(id: "1") {
    id
    name
    description
    logLevel
    analytics {
      name
      severity
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Review audit logs by user

See recent administrative actions, most recent first.

query RecentUserActivity {
  listAuditLogsByUser(input: { pageSize: 25 }) {
    items {
      date
      user
      op
      resourceId
      ips
    }
    pageInfo {
      next
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"query RecentUserActivity { listAuditLogsByUser(input: { pageSize: 25 }) { items { date user op resourceId ips } pageInfo { next } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
query RecentUserActivity {
  listAuditLogsByUser(input: { pageSize: 25 }) {
    items {
      date
      user
      op
      resourceId
      ips
    }
    pageInfo {
      next
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Making changes (mutations)

Resolve one or more alerts

Update the status of a batch of alerts by UUID.

mutation ResolveAlerts {
  updateAlerts(input: {
    uuids: ["6f250316-2cfb-4521-8cb7-bfaf46497bc5"]
    status: Resolved
  }) {
    items {
      uuid
      status
      updated
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"mutation ResolveAlerts { updateAlerts(input: { uuids: [\"6f250316-2cfb-4521-8cb7-bfaf46497bc5\"] status: Resolved }) { items { uuid status updated } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
mutation ResolveAlerts {
  updateAlerts(input: {
    uuids: ["6f250316-2cfb-4521-8cb7-bfaf46497bc5"]
    status: Resolved
  }) {
    items {
      uuid
      status
      updated
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Valid status values are New, InProgress, Resolved, and AutoResolved.

Assign a computer to a plan

mutation AssignComputerToPlan {
  setComputerPlan(uuid: "COMPUTER-UUID", input: { plan: "PLAN-ID" }) {
    uuid
    hostName
    plan {
      id
      name
    }
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"mutation AssignComputerToPlan { setComputerPlan(uuid: \"COMPUTER-UUID\", input: { plan: \"PLAN-ID\" }) { uuid hostName plan { id name } } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
mutation AssignComputerToPlan {
  setComputerPlan(uuid: "COMPUTER-UUID", input: { plan: "PLAN-ID" }) {
    uuid
    hostName
    plan {
      id
      name
    }
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Create a prevent list (block execution)

Block by Team ID, file hash, CDHash, or signing ID. This example blocks a specific developer Team ID.

mutation BlockByTeamId {
  createPreventList(input: {
    name: "Blocked developers"
    type: TEAMID
    list: ["ABCDE12345"]
    tags: []
    description: "Team IDs blocked from execution"
  }) {
    id
    name
    type
    list
    count
  }
}

Send that query over HTTP. Switch tabs for cURL or Python:

curl --request POST \
  --url "https://{region}.api.jamfcloud.com/protect/graphql" \
  --header "Authorization: Bearer <ACCESS-TOKEN>" \
  --header "X-Tenant-Id: <YOUR-TENANT-ID>" \
  --header "Content-Type: application/json" \
  --data '{"query":"mutation BlockByTeamId { createPreventList(input: { name: \"Blocked developers\" type: TEAMID list: [\"ABCDE12345\"] tags: [] description: \"Team IDs blocked from execution\" }) { id name type list count } }"}'
import requests

resp = requests.post(
    "https://{region}.api.jamfcloud.com/protect/graphql",
    headers={
        "Authorization": "Bearer <ACCESS-TOKEN>",
        "X-Tenant-Id": "<YOUR-TENANT-ID>",
        "Content-Type": "application/json",
    },
    json={"query": """
mutation BlockByTeamId {
  createPreventList(input: {
    name: "Blocked developers"
    type: TEAMID
    list: ["ABCDE12345"]
    tags: []
    description: "Team IDs blocked from execution"
  }) {
    id
    name
    type
    list
    count
  }
}
"""},
)
resp.raise_for_status()
print(resp.json())

Supported type values: TEAMID, FILEHASH, CDHASH, SIGNINGID.

Using variables

For production code, prefer GraphQL variables over string interpolation. Send query and variables in the request body:

{
  "query": "query Alerts($input: AlertQueryInput!) { listAlerts(input: $input) { items { uuid severity } pageInfo { next } } }",
  "variables": {
    "input": {
      "pageSize": 25,
      "filter": { "severity": { "equals": "High" } }
    }
  }
}
📘

These examples are grounded in the live Jamf Protect schema. See the Queries, Mutations, and Types pages for the complete field reference.


Jamf Protect API: Overview · Queries · Mutations · Types · Example Calls · Schema (SDL)



Did this page help you?